Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

c# – SortedSet.Remove() doesn't remove element returned by SortedSet.Min

I’m trying to implement a custom sorted Queue where items are sorted in this way:

  • If both items have the same parent path, sort them in FIFO manner
  • If items have different parent path, the item with bigger index should be dequeued first.

I’ve implemented it using the SortedSet. The problem is that while it generally works, in some cases, e.g. lots of adding and removing items at random, _mSortedSet.Remove(entry) returns false.

I’ve implemented it as such:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

public class CustomSortingQueue<T> : IComparer<(T work, string fullPath, ulong index)>
    {
        private readonly SortedSet<(T work, string fullPath, ulong index)> _mSortedSet;
        private readonly Comparer<ulong> _mIndexComparer = Comparer<ulong>.Default;

        private ulong _mNextIndex = 0;

        public CustomSortingQueue() {
            _mSortedSet = new SortedSet<(T work, string fullPath, ulong index)>(this);
        }

        public int Compare((T work, string fullPath, ulong index) x, (T work, string fullPath, ulong index) y)
        {
            if (x.fullPath == y.fullPath)
            {
                // For same paths we want to do normal counting - e.g. bigger index means should be done later
                return _mIndexComparer.Compare(x.index, y.index);
            }

            // For different paths, we want the new one to be done as fast as possible -> bigger index should be done faster
            return _mIndexComparer.Compare(y.index, x.index);
        }

        public void Enqueue(T work, string fullPath)
        {
            _mSortedSet.Add((work, fullPath, _mNextIndex++));
        }

        public (T work, string fullPath) Dequeue()
        {
            var entry = _mSortedSet.Min;
            _mSortedSet.Remove(entry);
            return (entry.work, entry.fullPath);
        }
    }

In the cases it fails I found that Remove returns false from sortedset.cs by failing if (Is2Node(current)).

Looking in the debugger at my _mSortedSet I can see that it has items like this (I’ll be skipping the exact values of the T parameter for brevity, as it’s unused in comparison):

(T, "Y:\\", 538),
(T, "Y:\\", 566),
(T, "Y:\\", 570),
...
(T, "X:\\", 537),
(T, "X:\\", 546),
(T, "X:\\", 595),
...
(T, null, 531),
(T, null, 532),
...
(T, null, 740),
(T, "Y:\\", 530),
(T, "Z:\\", 529)

The _mSortedSet.Min is (T, "Y:\\", 538). I see that the most probable culprit is the second to last item, which has a curious place in the output. My best guess is that something inside the sorted set breaks when recomputing the weighted tree.

I suspect this might be a problem in my Compare function (as is the case with most Remove problems) but I cannot pinpoint exactly why. Any help with understanding why the Remove fails even though Min is found would be greatly appreciated. Am I using the SortedSet for a wrong use-case?

>Solution :

The problem is that your comparer is not transitive, and comparers need to be transitive when sorting items.

The sorted collections all rely on there being a single valid ordering of items, and do not support collections with numerous valid orderings, which is the case given your requirements.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading