【问题标题】:First-in-first-out collection with maximum size and no duplication?具有最大大小且不重复的先进先出集合?
【发布时间】:2011-11-18 08:57:18
【问题描述】:

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

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

【问题讨论】:

标签: c# .net data-structures collections


【解决方案1】:

您必须创建一个实现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();
    }
}

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

【讨论】:

  • 一条评论,如果 !queue.Contains(item),它应该重新排队该项目(使其成为最近的)
  • 我假设您的意思是,如果在调用 Enqueue 时队列中已经存在该项目,Enqueue 方法应该将项目移动到队列顶部,对吗?
  • 为什么不使用现有的 Queue 而不是 List
  • 这个QueueSet存在性能问题,它使用了List.Contains(..)方法,实际上是一种顺序处理集合中所有项目的方法
  • @Artur Mustafin:我知道,但性能在这里并不重要,除非提问者 Louis Rhys 在问题中这么说。特别是如果结构将只存储五个文件名。在您的第二个问题中,最初代码使用 Queue 作为支持,但提问者的澄清评论迫使我使用 List 代替(请参阅编辑历史记录)。
【解决方案2】:

你想要queue,不是吗?

它并不真正支持最大大小和无重复,但您可以从 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);
        }
    }

包含或包装或 HAS-A 类可能如下所示:

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 中的内置集合继承是不切实际的。没有一个有用的方法被声明为virtual,所以它们不能被扩展。
  • @rtalbot:非通用版本似乎可以工作,但扩展Queue&lt;T&gt; 不起作用(因为我已经强调过)。你真的应该使用Queue&lt;T&gt;,但我想这可能会起作用。
  • 其实我不想抛出异常,只是将最近的对象出列
  • @Matthew 可能工作?它显然有效。而且我一直使用通用集合——但我试图回答这个问题。 :D
【解决方案3】:

看看这个:Limit size of Queue<T> in .NET?

你基本上需要一个Queue,如果你设法限制它的大小并让它“索引”或“唯一”那么你就可以了:)

我相信围绕Dictionary 的一些逻辑也可以,您将存储的数据类型是什么?字符串?

【讨论】:

  • 在这种情况下,是的,一个字符串。如何限制队列项是唯一的?
【解决方案4】:

这就是你想要的,HashQueue&lt;T&gt;,散列队列集。

添加了一些线程锁,防止意外锁定。请记住,所有 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);
            //}
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-11-21
    • 2011-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多