0

I want to implement a Priority Queue, with the ablity can specify the type of the queue(generic queue or unique queue).

public class PriorityQueue<TItem, TKey, TQueue> : IEnumerable<TItem>
    where TKey : IComparable
    where TQueue : Queue<TItem>, new ()
{
    private readonly Func<TItem, TKey> _keySelector;

    private readonly SortedDictionary<TKey, TQueue> _data = new SortedDictionary<TKey, TQueue>();

    public PriorityQueue(Func<TItem, TKey> keySelector)
    {
        _keySelector = keySelector;
    }

    public void Enqueue(TItem item)
    {
        var key = _keySelector(item);

        if (!_data.ContainsKey(key))
        {
            _data.Add(key, new TQueue());
        }

        var queue = _data[key];
        queue.Enqueue(item);
    }

    public TItem Dequeue()
    {
        if (IsEmpty)
        {
            throw new ArgumentException("Queue is EMPTY");
        }

        var key = _data.Keys.First();
        var queue = _data[key];

        var item = queue.Dequeue();
        if (queue.Count == 0)
        {
            _data.Remove(key);
        }

        return item;
    }

    public bool IsEmpty => _data.Count == 0;

    private int Count
    {
        get
        {
            var count = 0;
            foreach (var key in _data.Keys)
            {
                count += _data[key].Count;
            }

            return count;
        }
    }

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


    public IEnumerator<TItem> GetEnumerator()
    {
        return new Enumerator(_data);
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    private class Enumerator : IEnumerator<TItem>
    {
        private readonly SortedDictionary<TKey, TQueue> _dictionary;
        private readonly int _dictionaryCount;
        private int _currentPosition;

        public Enumerator(SortedDictionary<TKey, TQueue> data)
        {
            _dictionary = data;
            _dictionaryCount = DictionaryCount;
            _currentPosition = 0;
            Current = default(TItem);
        }

        private int DictionaryCount
        {
            get
            {
                var count = 0;
                foreach (var key in _dictionary.Keys)
                {
                    count += _dictionary[key].Count;
                }

                return count;
            }
        }

        public void Dispose()
        {

        }

        public bool MoveNext()
        {
            if (_currentPosition >= _dictionaryCount)
            {
                return false;
            }

            Current = GetCurrent();
            _currentPosition++;

            return true;
        }

        public void Reset()
        {
            _currentPosition = 0;
        }

        public TItem Current { get; private set; }

        private TItem GetCurrent()
        {
            var sum = 0;
            var item = default(TItem);

            foreach (var key in _dictionary.Keys)
            {
                var queue = _dictionary[key];

                sum += queue.Count;

                if (sum > _currentPosition)
                {
                    item = queue.Take(queue.Count - (sum - _currentPosition) + 1).Last();
                    break;
                }
            }

            return item;
        }

        object IEnumerator.Current => Current;
    }
}

and the unique queue

public class UniqueQueue<T> : Queue<T>
{
    private HashSet<T> _hashSet;

    public UniqueQueue()
    {
        _hashSet = new HashSet<T>();
    }

    public new void Enqueue(T item)
    {
        if (_hashSet.Add(item))
        {
            base.Enqueue(item);
        }
    }

    public new T Dequeue()
    {
        var item = base.Dequeue();
        _hashSet.Remove(item);
        return item;
    }

    public new void Clear()
    {
        _hashSet.Clear();
        base.Clear();
    }
}

But in the unit test, it fails with the following code

       [TestMethod]
    public void TestPriorityUniqueQueue()
    {
        var puq = new PriorityQueue<Node, int, UniqueQueue<Node>>(node => node.Key);

        var node1 = new Node(1, "One");
        var node2 = new Node(2, "Two");
        var node3 = new Node(1, "One 1");

        puq.Enqueue(node1);
        puq.Enqueue(node1);
        puq.Enqueue(node1);
        puq.Enqueue(node2);
        puq.Enqueue(node3);

        var list = new List<Node>();
        foreach (var node in puq)
        {
            list.Add(node);
        }

        Assert.AreEqual(list[0], node1);
        **Assert.AreEqual(list[1], node3);**
        Assert.AreEqual(list[2], node2);

        puq.Dequeue();
        puq.Dequeue();
        puq.Dequeue();

        Assert.IsTrue(puq.IsEmpty);
    }

I debug the test case and find when call the puq.Enqueue(node1), it calls the Enqueue() function from Queue, not my UniqueueQueue. But when I debug, I find that in the Enqueue of PriorityQueue, the variable is type of UniqueQueue, but it not call the Enqueue() of UniqueQueue. I really want to know why, or give me some advise about how this possible?

It fails in the bold line and the stack trace is

Assert.AreEqual failed. Expected:. Actual:. in ToolsTests.PriorityQueueTest.TestPriorityUniqueQueue() position somefolder\ToolsTests\PriorityQueueTests.cs:line number 118

the debug info of the queue variable from PriorityQueue<>.Enqueue() methord

  • Welcome to Stack Overflow. Can you provide the line on which it failed and the stack trace as well as the error message. It will help people understand your issue. – Dragonthoughts Nov 09 '18 at 09:00
  • 1
    Possible duplicate of [Overriding vs method hiding](https://stackoverflow.com/questions/3838553/overriding-vs-method-hiding) – dymanoid Nov 09 '18 at 09:21
  • Thanks Dragonthoughts, it fails in the third part code, the line Assert.AreEqual(list[1], node3); and the stack trace has nothing helpful – 赵乐赵乐 Nov 09 '18 at 11:17
  • Thanks dymanoid, I know the hiding rule. I was confused that when I new the TQueue in PriorityQueue's Enqueue() function, the TQueue should be UniqueueQueue, not Queue, it should call the Enqueue() of UniqueueQueue. So I think the problem is something of generic type parameter that I don't know. – 赵乐赵乐 Nov 09 '18 at 11:30

1 Answers1

0

Your generic type parameter TQueue<T> is expected to be of the type Queue<T> because you defined it in the generic constraint of the PriorityQueue:

 where TQueue : Queue<TItem>, new ()

Now whenever your code passes:

_data.Add(key, new TQueue());

It will create an instance of Queue<T> and not UniqueQueue<T>, hence it will never reach the Enqueue method of your UniqueQueue<T> as it calls the Enqueue method of Queue<T>.

Reasoning for this is because of the generic constraint (first code block above) you've supplied. It simply does not know the UniqueQueue<T> type, but rather the Queue<T> type, since that is what the PriorityQueue<TItem, TKey, TQueue> expects (because you 'told' him so). So whenever new TQueue() is executed, a new Queue<T> object will instantiated instead of UniqueQueue<T>.

In order for your tests to pass, and the PriorityQueue<TItem, TKey, TQueue> to actually use the UniqueQueue<T>, you must explicitly specify that you want to use the UniqueQueue<T> in the generic constraint:

where TQueue : UniqueQueue<TItem>, new()
Jevgeni Geurtsen
  • 3,133
  • 4
  • 17
  • 35
  • Oh, I see it. I pay attention on the debug info, when I print the queue variable in PriorityQueue<>.Enqueue() methord with `Debug.WriteLine(queue.GetType());`, it shows `CRSCD.ITP.Tools.UniqueQueue`1[CRSCD.ITP.ToolsTests.Node]`, but the decaration of the queue variable should be Queue, so it calls the Queue.Enqueue(). Thanks a lot Jevgeni. – 赵乐赵乐 Nov 09 '18 at 13:07