In C#, is there a queue which can only hold an object once in its lifetime?
我需要一个数据结构,这是一种特殊类型的队列。我希望这样,如果我的队列的一个实例曾经包含一个对象X,那么在这个实例中不可能再次将X排队。如果使用x调用排队方法,则不应执行任何操作,例如尝试向哈希集添加重复值。
示例用法:
1 2 3 4 5 6 7 8 9 10 | MyQueue<int> queue = new MyQueue<int>(); queue.Enqueue(5); queue.Enqueue(17); queue.Enqueue(28); queue.Enqueue(17); int firstNumber = queue.Dequeue(); queue.Enqueue(5); queue.Enqueue(3); List<int> queueContents = queue.ToList(); //this list should contain {17, 28, 3} |
我在msdn上四处看看,但找不到这样的课程。它是存在的,还是我必须自己实现它?
我想我也可以使用不同的数据结构,但访问总是FIFO,所以我认为队列将是最有效的。另外,我不知道还有什么其他的结构可以提供这种"实例生命周期的唯一性"特性。
我会做类似的事情:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | class UniqueQueue<T> { private readonly Queue<T> queue = new Queue<T>(); private HashSet<T> alreadyAdded = new HashSet<T>(); public virtual void Enqueue(T item) { if (alreadyAdded.Add(item)) { queue.Enqueue(item); } } public int Count { get { return queue.Count; } } public virtual T Dequeue() { T item = queue.Dequeue(); return item; } } |
注意,大部分代码都是从这个线程中借用的。
你必须自己去实现。
一种方法是在排队时将元素添加到
然后,当您想排队时,只需检查
由于您希望在队列的剩余生命周期内防止排队,因此可能不希望从
这只是韦恩答案的一个扩展版本,它只是稍微充实一点,并且支持更多的接口。(模拟
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | sealed class UniqueQueue<T> : IEnumerable<T>, ICollection, IEnumerable { private readonly Queue<T> queue; private readonly HashSet<T> alreadyAdded; public UniqueQueue(IEqualityComparer<T> comparer) { queue = new Queue<T>(); alreadyAdded = new HashSet<T>(comparer); } public UniqueQueue(IEnumerable<T> collection, IEqualityComparer<T> comparer) { //Do this so the enumeration does not happen twice in case the enumerator behaves differently each enumeration. var localCopy = collection.ToList(); queue = new Queue<T>(localCopy); alreadyAdded = new HashSet<T>(localCopy, comparer); } public UniqueQueue(int capacity, IEqualityComparer<T> comparer) { queue = new Queue<T>(capacity); alreadyAdded = new HashSet<T>(comparer); } //Here are the constructors that use the default comparer. By passing null in for the comparer it will just use the default one for the type. public UniqueQueue() : this((IEqualityComparer<T>) null) { } public UniqueQueue(IEnumerable<T> collection) : this(collection, null) { } public UniqueQueue(int capacity) : this(capacity, null) { } /// <summary> /// Attempts to enqueue a object, returns false if the object was ever added to the queue in the past. /// </summary> /// <param name="item">The item to enqueue</param> /// <returns>True if the object was successfully added, false if it was not</returns> public bool Enqueue(T item) { if (!alreadyAdded.Add(item)) return false; queue.Enqueue(item); return true; } public int Count { get { return queue.Count; } } public T Dequeue() { return queue.Dequeue(); } IEnumerator<T> IEnumerable<T>.GetEnumerator() { return ((IEnumerable<T>)queue).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)queue).GetEnumerator(); } void ICollection.CopyTo(Array array, int index) { ((ICollection)queue).CopyTo(array, index); } bool ICollection.IsSynchronized { get { return ((ICollection)queue).IsSynchronized; } } object ICollection.SyncRoot { get { return ((ICollection)queue).SyncRoot; } } } |
可以使用基本队列,但可以修改排队方法以验证以前输入的值。这里,我使用了一个哈希集来包含前面的值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
你的测试用例
1 2 3 4 5 6 7 8 9 10 | UniqueValueQueue<int> queue = new UniqueValueQueue<int>(); queue.Enqueue(5); queue.Enqueue(17); queue.Enqueue(28); queue.Enqueue(17); int firstNumber = queue.Dequeue(); queue.Enqueue(5); queue.Enqueue(3); List<int> queueContents = queue.ToList(); |
队列内容包含17、28和3。