5

.NET seems to have a lot of data structure and collection types. Does it have a first-in-first-out collection with a maximum size and no duplication, or something similar to that?

An example usage would be like for storing 5 most recently opened files. If a 6th object is added, dequeue the least recent object to keep the size to 5

Louis Rhys
  • 30,777
  • 53
  • 137
  • 211

4 Answers4

3

You'll have to create a QueueSet that implements ICollection<T>. It can be a wrapper class that contains a collection as a backing store. It can be implemented as follows:

class QueueSet<T> : ICollection<T> 
{
    List<T> queue=new List<T>();
    int maximumSize;

    public QueueSet(int maximumSize){
        if(maximumSize<0)
            throw new ArgumentOutOfRangeException("maximumSize");
        this.maximumSize=maximumSize;
    }

    public T Dequeue(){
        if(queue.Count>0){
            T value=queue[0];
            queue.RemoveAt(0);
            return value;
        }
        return default(T);
    }

    public T Peek(){
        if(queue.Count>0){
            return queue[0];
        }
        return default(T);
    }

    public void Enqueue(T item){
        if(queue.Contains(item)){
            queue.Remove(item);
        }
        queue.Add(item);
        while(queue.Count>maximumSize){
            Dequeue();
        }
    }

    public int Count {
        get {
            return queue.Count;
        }
    }

    public bool IsReadOnly {
        get {
            return false;
        }
    }

    public void Add(T item)
    {
        Enqueue(item);
    }

    public void Clear()
    {
        queue.Clear();
    }

    public bool Contains(T item)
    {
        return queue.Contains(item);
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        foreach(T value in queue){
            if(arrayIndex>=array.Length)break;
            if(arrayIndex>=0){
                array[arrayIndex]=value;
            }
            arrayIndex++;
        }
    }

    public bool Remove(T item)
    {
        if(Object.Equals(item,Peek())){
           Dequeue();
           return true;
        } else {
            return false;
        }
    }

    public IEnumerator<T> GetEnumerator()
    {
        return queue.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return queue.GetEnumerator();
    }
}

I release this code into the public domain.

Peter O.
  • 28,965
  • 14
  • 72
  • 87
  • one comment, if !queue.Contains(item), it should requeue the item (to make it the recent one) – Louis Rhys Sep 16 '11 at 03:36
  • I assume you mean that the Enqueue method should move the item to the top of the queue if the item already exists in the queue when Enqueue is called, am I right? – Peter O. Sep 16 '11 at 03:40
  • Why not using existing, Queue instead of a List? – Artur Mustafin Sep 16 '11 at 05:19
  • This QueueSet has performance issues, it uses List.Contains(..) method, which is actually a way to process all items in a collection sequentually – Artur Mustafin Sep 16 '11 at 05:21
  • @Artur Mustafin: I know, but performance is unimportant here unless the asker, Louis Rhys, states so in the question. Especially if the structure will only store five filenames. In your second question, originally the code used a Queue as backing, but the clarifying comment by the asker forced me to use a List instead (see the edit history). – Peter O. Sep 16 '11 at 05:31
1

You want a queue, no?

It doesn't really support maximum size and no duplication but you could inherit from Queue and add those features.

You could do so by overriding the Enqueue method. Something like this might work (but throw more appropriate exception types!):

    public class LimitedQueue : Queue
    {
        public override void Enqueue(object obj)
        {
            if (this.Count > 5)
                throw new Exception();

            if(this.Contains(obj))
                throw new Exception();
            base.Enqueue(obj);
        }
    }

A containing or wrapper or HAS-A class might look like this:

public class QueueWrapper<T>
{
     private Queue<T> _queue;
     public void Enqueue(T item)
     {
         if (_queue.Count > 5)
             throw new Exception();

         if(this.Contains(item))
             throw new Exception();

         _queue.Enqueue(item);
     }

     //Any other methods you might want to use would also need to be exposed similarly
}
rtalbot
  • 1,556
  • 9
  • 13
  • a queue allows duplicates and does not have a maximum limitation – Louis Rhys Sep 16 '11 at 02:58
  • Actually, inheriting from built-in collections in .NET isn't practical. None of the useful methods are declared `virtual`, so they can't be extended. – Matthew Scharley Sep 16 '11 at 02:59
  • @rtalbot: The non-generic version seems to workg, but extending `Queue` doesn't work (for the reason I've already highlighted). You really should be using `Queue`, but I suppose this could potentially work. – Matthew Scharley Sep 16 '11 at 03:03
  • actually, I don't want to throw exception, just dequeue the least recent object – Louis Rhys Sep 16 '11 at 03:05
  • @Matthew Potentially work? It clearly works. And I use generic collections all the time - but I was trying to answer the question. :D – rtalbot Sep 16 '11 at 03:06
  • @rtalbot: Like I said, your non-generic version works. You really should be subclassing `Queue` though, not `Queue` (they are two different classes), for type safety and all that loveliness that generics provides. *That* version doesn't work as `Enqueue(T)` isn't declared as `virtual` and hence can't be `override`n. ''LimitedQueue.Enqueue(T)': cannot override inherited member 'System.Collections.Generic.Queue.Enqueue(T)' because it is not marked virtual, abstract, or override' – Matthew Scharley Sep 16 '11 at 03:08
  • What will happens if somebody casts the LimitedQueue to a Queue and call Enqueue(), or another method inside the original Queue calls Enqueue? – Louis Rhys Sep 16 '11 at 03:10
  • @Louis: and this is why you want to override. If you override, then your version gets called. If you don't, then the parents version does. – Matthew Scharley Sep 16 '11 at 03:11
  • ouch.. would it be practical to create a wrapper class? – Louis Rhys Sep 16 '11 at 03:14
  • @Louis - yes, you could also write a wrapper class but you'll have to implement more methods to call the Queue methods you want to expose. – rtalbot Sep 16 '11 at 03:21
  • @Louis - I've amended my answer to include a simple class which wraps Queue. – rtalbot Sep 16 '11 at 03:26
0

have a look at this: Limit size of Queue<T> in .NET?

you basically need a Queue, if you manage to limit its size and get it "indexed" or "unique" then you are ok :)

I believe a bit of logic around a Dictionary would also work, what is the data type you will store? Strings?

Community
  • 1
  • 1
Davide Piras
  • 42,120
  • 10
  • 86
  • 140
0

This is what you want, HashQueue<T>, the hashed queued set.

Added some thread locks, protects from accidental locks. Keep in mind, that all HashSet operation breaks the order of existing queue.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Security.Permissions;

namespace ConsoleApplication
{
    internal class Program
    {
        [Serializable]
        private class HashQueue<T> : ISerializable, IDeserializationCallback, ISet<T>, ICollection<T>, IEnumerable<T>, IEnumerable
        {
            private int _maxCount;
            private Queue<T> _queue = new Queue<T>();
            private HashSet<T> _set = new HashSet<T>();

            public HashQueue(int maxCount = 0)
            {
                if (maxCount < 0) throw new ArgumentOutOfRangeException("maxCount");
                _maxCount = maxCount;
            }

            public bool Add(T item)
            {
                lock (this)
                {
                    if (_queue.Count == _maxCount)
                    {
                        _set.Remove(_queue.Dequeue());
                    }
                    if (_set.Add(item))
                    {
                        _queue.Enqueue(item);
                        return true;
                    }
                    return false;
                }
            }

            public bool Remove(T item)
            {
                lock (this)
                {
                    if (object.ReferenceEquals(_queue.Peek(), item))
                    {
                        return _set.Remove(_queue.Dequeue());
                    }
                    return false;
                }
            }

            public void Clear()
            {
                lock (this)
                {
                    _set.Clear();
                    _queue.Clear();
                }
            }

            public bool Contains(T item)
            {
                lock (this)
                {
                    return _set.Contains(item);
                }
            }

            public void CopyTo(T[] array, int arrayIndex)
            {
                lock (this)
                {
                    _queue.CopyTo(array, arrayIndex);
                }
            }

            public int Count
            {
                get
                {
                    lock (this)
                    {
                        return _queue.Count;
                    }
                }
            }

            public bool IsReadOnly
            {
                get
                {
                    return false;
                }
            }

            public void ProcessItems(Action<T> action)
            {
                lock (this)
                {
                    foreach (T item in _queue)
                    {
                        action(item);
                    }
                }
            }

            void ICollection<T>.Add(T item)
            {
                lock (this)
                {
                    if (_queue.Count == _maxCount)
                    {
                        _set.Remove(_queue.Dequeue());
                    }
                    if (!_set.Add(item))
                    {
                        throw new ArgumentOutOfRangeException("item");
                    }
                    _queue.Enqueue(item);
                }
            }

            public IEnumerator<T> GetEnumerator()
            {
                lock (this)
                {
                    return _queue.GetEnumerator();
                }
            }

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                lock (this)
                {
                    return (IEnumerator)GetEnumerator();
                }
            }

            public void OnDeserialization(object sender)
            {
                lock (this)
                {
                    _set.OnDeserialization(sender);
                }
            }

            private void RebuildQuery()
            {
                _queue.Clear();
                foreach (T item in _set)
                {
                    _queue.Enqueue(item);
                }
            }

            public void ExceptWith(IEnumerable<T> other)
            {
                lock (this)
                {
                    _set.ExceptWith(other);
                    RebuildQuery();
                }
            }

            public void IntersectWith(IEnumerable<T> other)
            {
                lock (this)
                {
                    _set.IntersectWith(other);
                    RebuildQuery();
                }
            }

            public bool IsProperSubsetOf(IEnumerable<T> other)
            {
                lock (this)
                {
                    return _set.IsProperSubsetOf(other);
                }
            }

            public bool IsProperSupersetOf(IEnumerable<T> other)
            {
                lock (this)
                {
                    return _set.IsProperSupersetOf(other);
                }
            }

            public bool IsSubsetOf(IEnumerable<T> other)
            {
                lock (this)
                {
                    return _set.IsSubsetOf(other);
                }
            }

            public bool IsSupersetOf(IEnumerable<T> other)
            {
                lock (this)
                {
                    return _set.IsSupersetOf(other);
                }
            }

            public bool Overlaps(IEnumerable<T> other)
            {
                lock (this)
                {
                    return _set.Overlaps(other);
                }
            }

            public bool SetEquals(IEnumerable<T> other)
            {
                lock (this)
                {
                    return _set.SetEquals(other);
                }
            }

            public void SymmetricExceptWith(IEnumerable<T> other)
            {
                lock (this)
                {
                    _set.SymmetricExceptWith(other);
                    RebuildQuery();
                }
            }

            public void UnionWith(IEnumerable<T> other)
            {
                lock (this)
                {
                    _set.UnionWith(other);
                    RebuildQuery();
                }
            }

            [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
            void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
            {
                _set.GetObjectData(info, context);
            }
        }

        private static void Main(string[] args)
        {
            HashQueue<int> queue = new HashQueue<int>(5);
            queue.Add(1);
            queue.Add(2);
            queue.Add(3);
            queue.Add(4);
            queue.Add(5);
            queue.Add(6);
            queue.ProcessItems((i) => Console.Write(i));
            //foreach (int i in queue)
            //{
            //    Console.Write("{0}", i);
            //}
        }
    }
}
Artur Mustafin
  • 2,195
  • 13
  • 19