具有最大大小和无重复项的先进先出集合?

6

.NET似乎有很多数据结构和集合类型。它是否具有具有最大大小且没有重复项的先进先出集合,或类似于此的内容?

一个示例用法可能是存储5个最近打开的文件。如果添加第6个对象,则弹出最不常用的对象以将大小保持为5。


你尝试过带有排序功能的Dictionary<T, int>吗? - Alan Turing
添加了一个排队的哈希集合的代码示例。 - Alan Turing
可能是如何在C# .NET中使用"FIFO"?的重复问题。 - Jim Fell
4个回答

3
您需要创建一个实现 ICollection<T>QueueSet。它可以是一个包含一个集合作为后备存储的包装类。可以按照以下方式实现:
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();
    }
}

我将此代码发布到公共领域。

如果队列中不包含该项,应重新排队该项(使其成为最近的项)。 - Louis Rhys
我猜你的意思是,如果在调用Enqueue方法时项目已经存在于队列中,那么Enqueue方法应该将该项目移动到队列顶部,我说得对吗? - Peter O.
为什么不使用现有的Queue<T>而是用List<T>呢? - Alan Turing
该 QueueSet 存在性能问题,它使用了 List<T>.Contains(..) 方法,这实际上是一种按顺序处理集合中所有项目的方式。 - Alan Turing
@Artur Mustafin:我知道,但是性能在这里并不重要,除非提问者Louis Rhys在问题中明确说明。特别是如果该结构仅存储五个文件名。在您的第二个问题中,最初代码使用了队列作为支持,但是请教者的澄清评论迫使我改用了List(请参见编辑历史记录)。 - Peter O.

1
你想要一个队列,对吧?
它并不支持最大尺寸和无重复,但你可以继承自Queue并添加这些特性。
你可以通过覆盖Enqueue方法来实现。类似这样的代码可能会起作用(但要抛出更合适的异常类型!):
    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);
        }
    }

一个包含或包装其他类的类可能长成这样:
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
}

实际上,在.NET中继承内置集合并不实际。没有任何有用的方法被声明为“虚拟”,因此它们无法被扩展。 - Matthew Scharley
@rtalbot:非泛型版本似乎可以工作,但扩展Queue<T>不起作用(因为我已经强调过的原因)。你真的应该使用Queue<T>,但我想这可能有潜力。 - Matthew Scharley
@Matthew 可能有效吗?它显然是有效的。我经常使用通用集合 - 但我试图回答这个问题。 :D - rtalbot
哎呀.. 创建一个包装类会不会实用? - Louis Rhys
@Louis - 是的,你也可以编写一个包装类,但是你需要实现更多的方法来调用你想要公开的 Queue<T> 方法。 - rtalbot
显示剩余6条评论

0
这就是你想要的HashQueue<T>,哈希队列集合。
添加了一些线程锁,以防止意外锁定。请记住,所有HashSet操作都会破坏现有队列的顺序。
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);
            //}
        }
    }
}

0

看一下这个:如何在.NET中限制Queue<T>的大小?

你基本上需要一个Queue,如果你设法限制它的大小并使其“索引化”或“唯一化”,那么就没问题了 :)

我相信围绕一个Dictionary的一些逻辑也可以解决问题,你将存储什么数据类型?字符串吗?


在这种情况下,是的,一个字符串。如何限制队列项为唯一? - Louis Rhys

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接