I need a datastructure, which is a special type of queue. I want that, if an instance of my queue ever contained an object X, it shouldn't be possible to enqueue X again in this instance. The enqueuing method should just do nothing if called with X, like the attempt to add a duplicate value to a HashSet.
Example usage:
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}
I looked around on MSDN, but couldn't find such a class. Does it exist, or do I have to implement it myself?
I guess I could use a different data structure too, but access will always be FIFO, so I thought a queue will be most efficient. Also, I don't know of any other structure which provides such "uniqueness over instance lifetime" feature.
I would do something similar to this:
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;
}
}
Note, most of this code was borrowed from This Thread.
You'd have to implement that yourself.
One idea is just to add the element to a HashSet
when you enqueue it.
Then, when you want to enqueue, just check the HashSet
for the item, if it exists, don't enqueue.
Since you want to prevent enqueuing for the rest of the queue's lifetime, you probably won't want to ever remove from the HashSet
.
This is just a extended version of wayne's answer, it is just a little more fleshed out and having a few more interfaces supported. (To mimic Queue<T>
's interfaces)
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; }
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With