Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In C#, is there a queue which can only hold an object once in its lifetime?

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.

like image 495
Rumi P. Avatar asked Nov 13 '13 14:11

Rumi P.


3 Answers

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.

like image 171
Wayne Pincence Avatar answered Oct 18 '22 22:10

Wayne Pincence


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.

like image 36
Bernhard Barker Avatar answered Oct 18 '22 22:10

Bernhard Barker


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; }
    }
}
like image 31
Scott Chamberlain Avatar answered Oct 18 '22 22:10

Scott Chamberlain