Is there a list that is sorted automatically in .NET?

前端 未结 8 1182
轻奢々
轻奢々 2021-02-05 04:00

I have a collection of Layers where they have names and colors. What I want to do is to sort these first based on colors, then based on their names:



        
8条回答
  •  再見小時候
    2021-02-05 04:22

    A little late to the party, but up for posterity's sake.

    in order to optimise separation of concerns, I wrote a wrapper class which keeps a list sorted (and allows duplicates), as below:

    public class OrderedList : IList, ICollection, IList, ICollection, IReadOnlyList, IReadOnlyCollection, IEnumerable, IEnumerable
    {
        #region Fields
        readonly List _list;
        readonly IComparer _comparer;
        #endregion
    
        #region Constructors
        OrderedList(List list, IComparer comparer)
        {
            _list = list;
            _comparer = comparer;
        }
        public OrderedList() 
            : this(new List(), Comparer.Default)
        {
        }
        public OrderedList(IComparer comparer)
            : this(new List(), comparer)
        {
        }
        public OrderedList(IEnumerable collection)
            : this(collection, Comparer.Default)
        {
        }
    
        public OrderedList(IEnumerable collection, IComparer comparer)
            : this(new List(collection), comparer)
        {
            _list.Sort(comparer);
        }
    
        public OrderedList(int capacity)
            : this(new List(capacity), Comparer.Default)
        {
        }
        public OrderedList(int capacity, IComparer comparer)
            : this(new List(capacity), comparer)
        {
        }
        //yet to be implemented
        //public void OrderedList(Comparison comparison);
    
        #endregion
    
        #region Properties
        public int Capacity { get { return _list.Capacity; } set { _list.Capacity = value; } }
        public int Count { get { return _list.Count; } }
        object IList.this[int index] { get { return _list[index]; } set { _list[index] = (T)value; } }
        public T this[int index] { get { return _list[index]; } set { _list[index] = value; } }
        //public bool IsSynchronized { get { return false; } }
        bool ICollection.IsSynchronized { get { return false; } }
        //public object SyncRoot { get { return _list; } }
        object ICollection.SyncRoot { get { return _list; } } //? should return this 
        bool IList.IsFixedSize { get { return false; } }
        bool IList.IsReadOnly { get { return false; } }
        bool ICollection.IsReadOnly { get { return false; } }
        #endregion
    
        #region Methods
        void ICollection.Add(T item)
        {
            Add(item);
        }
        /// 
        /// Adds a new item to the appropriate index of the SortedList
        /// 
        /// The item to be removed
        /// The index at which the item was inserted
        public int Add(T item)
        {
            int index = BinarySearch(item);
            if (index < 0)
            {
                index = ~index;
            }
            _list.Insert(index, item);
            return index;
        }
        int IList.Add(object item)
        {
            return Add((T)item);
        }
        //NOT performance tested against other ways algorithms yet
        public void AddRange(IEnumerable collection)
        {
            var insertList = new List(collection);
            if (insertList.Count == 0) 
            {
                return;
            }
            if (_list.Count == 0) 
            { 
                _list.AddRange(collection);
                _list.Sort(_comparer);
                return;
            }
            //if we insert backwards, index we are inserting at does not keep incrementing
            insertList.Sort(_comparer);
            int searchLength = _list.Count;
            for (int i=insertList.Count-1;i>=0;i--)
            {
                T item = insertList[i];
                int insertIndex = BinarySearch(0, searchLength, item);
                if (insertIndex < 0)
                {
                    insertIndex = ~insertIndex;
                }
                else
                {
                    while (--insertIndex>=0 && _list[insertIndex].Equals(item)) { }
                    insertIndex++;
                }
                if (insertIndex<=0)
                {
                    _list.InsertRange(0, insertList.GetRange(0, i+1 ));
                    break;
                }
                searchLength = insertIndex-1;
                item = _list[searchLength];
                int endInsert = i;
                while (--i>=0 && _comparer.Compare(insertList[i], item) > 0) { }
                i++;
                _list.InsertRange(insertIndex, insertList.GetRange(i, endInsert - i +1));
            }
        }
        public int BinarySearch(T item)
        {
            return _list.BinarySearch(item, _comparer);
        }
        public int BinarySearch(int index, int count, T item)
        {
            return _list.BinarySearch(index,count,item, _comparer);
        }
        public ReadOnlyCollection AsReadOnly()
        {
            return _list.AsReadOnly();
        }
        public void Clear() { _list.Clear(); }
        public bool Contains(T item) { return BinarySearch(item) >= 0; }
        bool IList.Contains(object item)
        {
            return Contains((T)item);
        }
        public List ConvertAll(Converter converter) { return _list.ConvertAll(converter); }
        public void CopyTo(T[] array) { _list.CopyTo(array); }
        public void CopyTo(T[] array, int arrayIndex) { _list.CopyTo(array,arrayIndex); }
        void ICollection.CopyTo(Array array, int arrayIndex) { _list.CopyTo((T[])array, arrayIndex); }
        public void CopyTo(int index, T[] array, int arrayIndex, int count) { _list.CopyTo(index, array, arrayIndex, count); }
        public void ForEach(Action action)
        {
            foreach (T item in _list)
            {
                action(item);
            }
        }
    
        IEnumerator IEnumerable.GetEnumerator() { return _list.GetEnumerator(); }
        public IEnumerator GetEnumerator() { return _list.GetEnumerator(); }
        public List GetRange(int index, int count) { return _list.GetRange(index,count); }
    
        public bool Remove(T item) 
        {
            int index = BinarySearch(item);
            if (index < 0)
            {
                return false;
            }
            _list.RemoveAt(index);
            return true;
        }
        void IList.Remove(object item)
        {
            Remove((T)item);
        }
    
        public void RemoveAt(int index) { _list.RemoveAt(index); }
        public void RemoveRange(int index, int count) { _list.RemoveRange(index, count); }
        public T[] ToArray() { return _list.ToArray(); }
        public void TrimExcess() { _list.TrimExcess(); }
        /// 
        /// Find the first index of the given item
        /// 
        /// 
        /// 
        public int IndexOf(T item)
        {
            int index = BinarySearch(item);
            if (index < 0) return -1;
            while(--index >= 0 && _list[index].Equals(item)){}
            return index+1;
        }
    
        int IList.IndexOf(object item)
        {
            return IndexOf((T)item);
        }
        /// 
        /// Find the last index of the given item
        /// 
        /// 
        /// 
        public int LastIndexOf(T item)
        {
            int index = BinarySearch(item);
            if (index < 0) return -1;
            while (++index < _list.Count && _list[index].Equals(item)) { }
            return index-1;
        }
    
        /// 
        /// Return all values within bounds specified
        /// 
        /// Minimum Bound
        /// Maximum Bound
        /// subset of list with values within or equal to bounds specified
        public T[] WithinRange(T min, T max)
        {
            if (_comparer.Compare(min,max) > 0)
            {
                throw new ArgumentException("min must be <= max");
            }
            int minSearchLength;
            int maxIndex = _list.BinarySearch(max, _comparer);
            if (maxIndex >= 0)
            {
                minSearchLength = maxIndex + 1;
                while (++maxIndex < _list.Count && _comparer.Compare(max, _list[maxIndex]) == 0) { }
                --maxIndex;
            }
            else
            {
                minSearchLength = ~maxIndex;
                if (minSearchLength <= 0)
                {
                    return new T[0];
                }
                maxIndex = minSearchLength - 1;
            }
    
            int minIndex = _list.BinarySearch(0, minSearchLength, min, _comparer);
            if (minIndex >= 0)
            {
                while (--minIndex >= 0 && _comparer.Compare(max, _list[minIndex]) == 0) { }
                ++minIndex;
            }
            else
            {
                minIndex = ~minIndex;
                if (minIndex > maxIndex)
                {
                    return new T[0];
                }
            }
            int length = maxIndex - minIndex + 1;
            var returnVar = new T[length];
            _list.CopyTo(minIndex, returnVar, 0, length);
            return returnVar;
    
        }
        #endregion
    
        #region NotImplemented
        const string _insertExceptionMsg = "SortedList detemines position to insert automatically - use add method without an index";
        void IList.Insert(int index, object item)
        {
            throw new NotImplementedException(_insertExceptionMsg);
        }
        void IList.Insert(int index, T item)
        {
            throw new NotImplementedException(_insertExceptionMsg);
        }
        #endregion
    }
    

    Tests written are not extensive (or pretty) but are included in case anyone wanted to expand on them

    [TestClass]
    public class TestOrderedList
    {
        [TestMethod]
        public void TestIntegerList()
        {
            var startList = new List(new int[] { 5, 2, 1, 4, 5, 5, 2 });
            var olist = new OrderedList(startList);
            startList = startList.OrderBy(l => l).ToList();
            CollectionAssert.AreEqual(startList, olist);
            Assert.AreEqual(0, olist.Add(0));
            int nextInc = olist.Max() + 1;
            Assert.AreEqual(olist.Count, olist.Add(nextInc));
            CollectionAssert.AreEqual(startList.Concat(new int[] { 0, nextInc }).OrderBy(l => l).ToList(), olist);
            Assert.IsTrue(olist.Remove(0));
            Assert.IsFalse(olist.Remove(0));
            Assert.IsTrue(olist.Remove(nextInc));
            CollectionAssert.AreEqual(startList, olist);
    
            var addList = new List(new int[] { 5, -1, 2, 2, -1, 3, 2 });
            olist.AddRange(addList);
            addList = startList.Concat(addList).OrderBy(l => l).ToList();
            CollectionAssert.AreEqual(addList, olist);
            olist.Remove(-1);
            addList.Remove(-1);
            CollectionAssert.AreEqual(addList, olist);
            olist.Remove(2);
            addList.Remove(2);
            CollectionAssert.AreEqual(addList, olist);
    
            olist = new OrderedList();
            int[] seed = new int[] { -2, -2 };
            olist.AddRange(seed);
            CollectionAssert.AreEqual(seed, olist);
            olist.AddRange(new int[] { });
            olist.AddRange(new int[] { -2 });
            CollectionAssert.AreEqual(seed.Concat(new int[] { -2 }).ToList(), olist);
            olist.AddRange(new int[] { -3 });
            CollectionAssert.AreEqual((new int[] { -3, -2 }).Concat(seed).ToList(), olist);
        }
    
        [TestMethod]
        public void TestIndexOf()
        {
            var test = new OrderedList(new[] { 0, -1, -2 });
            Assert.AreEqual(0, test.IndexOf(-2));
            Assert.AreEqual(2, test.IndexOf(0));
            test.Add(-2);
            Assert.AreEqual(0, test.IndexOf(-2));
            Assert.AreEqual(1, test.LastIndexOf(-2));
            test.Add(0);
            Assert.AreEqual(3, test.IndexOf(0));
            Assert.AreEqual(4, test.LastIndexOf(0));
        }
    
        [TestMethod]
        public void TestRangeFinding()
        {
            var test = new OrderedList { 2 };
            CollectionAssert.AreEqual(new[] { 2 }, test.WithinRange(0, 6));
            CollectionAssert.AreEqual(new[] { 2 }, test.WithinRange(0, 2));
            CollectionAssert.AreEqual(new[] { 2 }, test.WithinRange(2, 4));
            CollectionAssert.AreEqual(new int[0], test.WithinRange(-6, 0));
            CollectionAssert.AreEqual(new int[0], test.WithinRange(6, 8));
    
            test = new OrderedList();
            CollectionAssert.AreEqual(new int[0], test.WithinRange(6, 8));
    
            test = new OrderedList{ -4, -2, 0 ,4, 6, 6 };
            CollectionAssert.AreEqual(new[] { 0, 4 }, test.WithinRange(0, 4));
            CollectionAssert.AreEqual(new[] { 0, 4 }, test.WithinRange(-1, 5));
            CollectionAssert.AreEqual(new[] { 6, 6 }, test.WithinRange(6, 8));
            CollectionAssert.AreEqual(new[] { 6, 6 }, test.WithinRange(5, 8));
            CollectionAssert.AreEqual(new[] { -4, -2 }, test.WithinRange(-5, -1));
            CollectionAssert.AreEqual(new[] { -4, }, test.WithinRange(-4, -3));
            CollectionAssert.AreEqual(new int[0], test.WithinRange(-6, -5));
    
            Assert.ThrowsException(() => test.WithinRange(6, 4));
    
        }
    }
    

提交回复
热议问题