关于数据结构:在C#中,是否有一个队列只能在其生命周期内保存一次对象?

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

我需要一个数据结构,这是一种特殊类型的队列。我希望这样,如果我的队列的一个实例曾经包含一个对象X,那么在这个实例中不可能再次将X排队。如果使用x调用排队方法,则不应执行任何操作,例如尝试向哈希集添加重复值。

示例用法:

1
2
3
4
5
6
7
8
9
10
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}

我在msdn上四处看看,但找不到这样的课程。它是存在的,还是我必须自己实现它?

我想我也可以使用不同的数据结构,但访问总是FIFO,所以我认为队列将是最有效的。另外,我不知道还有什么其他的结构可以提供这种"实例生命周期的唯一性"特性。


我会做类似的事情:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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;
    }
}

注意,大部分代码都是从这个线程中借用的。


你必须自己去实现。

一种方法是在排队时将元素添加到HashSet中。

然后,当您想排队时,只需检查HashSet中的项目,如果它存在,就不要排队。

由于您希望在队列的剩余生命周期内防止排队,因此可能不希望从HashSet中删除。


这只是韦恩答案的一个扩展版本,它只是稍微充实一点,并且支持更多的接口。(模拟Queue的接口)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
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; }
    }
}


可以使用基本队列,但可以修改排队方法以验证以前输入的值。这里,我使用了一个哈希集来包含前面的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class UniqueValueQueue<T> : Queue<T>
{
    private readonly HashSet<T> pastValues = new HashSet<T>();

    public new void Enqueue(T item)
    {
        if (!pastValues.Contains(item))
        {
            pastValues.Add(item);

            base.Enqueue(item);
        }
    }
}

你的测试用例

1
2
3
4
5
6
7
8
9
10
UniqueValueQueue<int> queue = new UniqueValueQueue<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();

队列内容包含17、28和3。