是否有一个在.NET中自动排序的列表?
我有一个Layers
的集合,他们有名称和颜色。 我想要做的是先根据颜色对它们进行排序,然后根据它们的名称进行排序:
class Layer { public string Name {get; set;} public LayerColor Color {get; set;} } enum LayerColor { Red, Blue, Green }
喜欢:
(red) layer2 (red) layer7 (blue) layer0 (blue) layer3 ...
我正在查看SortedList,但它像一个字典,所以不允许重复项。
此外,我正在使用API,我按创建顺序获取Layers
列表,因此我需要获取Layers
的完整列表,以便按照我想要的方式对它们进行排序。
最终, Layers
列表将绑定到WPF UI,用户可以在其中添加新图层,这就是为什么我希望始终对内部列表进行排序,因为性能不重要( Layers
数小于一千)。
最后,我将按以下方式访问我排序的Layers
:
class Image { public MySortedList Layers {get; set;} }
最好的方法是什么?
你找到了吗? 通用SortedList和SortedList 。
所以我错过了重复部分,这让我更加努力。 但这是我将如何解决它:
var sortedList = new SortedList>(); var redSortedList = new SortedList(); // Add all layers associated with the color red sortedList.Add(LayerColor.Red, redSortedList);
这对你有用吗 此外,我更喜欢使用linq但如果你真的想要一个排序列表我的解决方案很可能会工作。
最后一次尝试:):
public class YourClass { private List _layers; public List Layers { get { _layers = _layers.OrderBy(y => y.LayerColor).ThenBy(y => y.Name).ToList(); return _layers; } set { _layers = value; } } }
请注意,我直接在浏览器中编写而不在VS中测试它(坐在OS X上),但您可能会明白这一点。
派对有点晚了,但出于后人的缘故。
为了优化关注点的分离,我编写了一个包装类,它将列表排序(并允许重复),如下所示:
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 }
所写的测试并不广泛(或相当),但包括在任何人想要扩展它们的情况下
[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)); } }
你走在正确的轨道上。 我将创建一个inheritance自Collection的自定义集合类。 在此自定义集合中,您可以覆盖插入/删除方法,并在添加/删除项目时对集合进行排序。
您可以使用常规List
,但在显示列表之前和添加新值之后调用Sort()
方法。 这应该为您提供所需的function。 该应用程序的性能足够好。
当然,您必须定义自己的比较才能使用,但这不应该太麻烦。
如果您没有可用于对列表进行排序的add事件的任何挂钩,那么您可以将列表包装在@Justin推荐的自定义集合类中。
使用System.Linq,执行:
from layer in layers orderby layer.Color, layer.Name select layer
如果排序仅用于显示目的,请让WPF处理它:
ICollectionView view = CollectionViewSource.GetDefaultView(Layers); view.SortDescriptions.Add(new SortDescription("Color", ListSortDirection.Ascending); view.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending);
然后将Layers
绑定到UI ItemsControl
。
首先在Layer中实现IComparable接口并声明CompareTo方法。 然后使用SortedList集合来存储您的对象。
public class Layer : IComparable { public int CompareTo(object obj) { //return -1 if this is before obj, 0 is same, 1 is after. } }
你可以使用arraylist并在linq查询下面进行排序
ArrayList myList = new ArrayList(); Layer obj1 = new Layer(); obj1.Color = LayerColor.Red; obj1.Name = "Layer1"; myList.Add(obj1); Layer obj2 = new Layer(); obj2.Color = LayerColor.Green; obj2.Name = "Layer2"; myList.Add(obj2); Layer obj3 = new Layer(); obj3.Color = LayerColor.Blue; obj3.Name = "Layer3"; myList.Add(obj3); Layer obj4 = new Layer(); obj4.Color = LayerColor.Green; obj4.Name = "Layer4"; myList.Add(obj4); var mySortedList = myList.OfType().OrderBy(l => l.Color) .ThenBy(l => l.Name);