我有一个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
的数量不到一千个。
最后,{I}排序的Layers
将通过以下方式访问:
class Image
{
public MySortedList<Layer> Layers {get; set;}
}
最好的方法是什么?
答案 0 :(得分:19)
派对有点晚了,但出于后人的缘故。
为了优化关注点的分离,我编写了一个包装类,它将列表排序(并允许重复),如下所示:
public class OrderedList<T> : IList<T>, ICollection<T>, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, IEnumerable
{
#region Fields
readonly List<T> _list;
readonly IComparer<T> _comparer;
#endregion
#region Constructors
OrderedList(List<T> list, IComparer<T> comparer)
{
_list = list;
_comparer = comparer;
}
public OrderedList()
: this(new List<T>(), Comparer<T>.Default)
{
}
public OrderedList(IComparer<T> comparer)
: this(new List<T>(), comparer)
{
}
public OrderedList(IEnumerable<T> collection)
: this(collection, Comparer<T>.Default)
{
}
public OrderedList(IEnumerable<T> collection, IComparer<T> comparer)
: this(new List<T>(collection), comparer)
{
_list.Sort(comparer);
}
public OrderedList(int capacity)
: this(new List<T>(capacity), Comparer<T>.Default)
{
}
public OrderedList(int capacity, IComparer<T> comparer)
: this(new List<T>(capacity), comparer)
{
}
//yet to be implemented
//public void OrderedList(Comparison<T> 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<T>.IsReadOnly { get { return false; } }
#endregion
#region Methods
void ICollection<T>.Add(T item)
{
Add(item);
}
/// <summary>
/// Adds a new item to the appropriate index of the SortedList
/// </summary>
/// <param name="item">The item to be removed</param>
/// <returns>The index at which the item was inserted</returns>
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<T> collection)
{
var insertList = new List<T>(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<T> 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<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> 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<T> action)
{
foreach (T item in _list)
{
action(item);
}
}
IEnumerator IEnumerable.GetEnumerator() { return _list.GetEnumerator(); }
public IEnumerator<T> GetEnumerator() { return _list.GetEnumerator(); }
public List<T> 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(); }
/// <summary>
/// Find the first index of the given item
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
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);
}
/// <summary>
/// Find the last index of the given item
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
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;
}
/// <summary>
/// Return all values within bounds specified
/// </summary>
/// <param name="min">Minimum Bound</param>
/// <param name="max">Maximum Bound</param>
/// <returns>subset of list with values within or equal to bounds specified</returns>
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<T>.Insert(int index, T item)
{
throw new NotImplementedException(_insertExceptionMsg);
}
#endregion
}
所写的测试并不广泛(或相当),但如果有人想要扩展它们,则包括在内
[TestClass]
public class TestOrderedList
{
[TestMethod]
public void TestIntegerList()
{
var startList = new List<int>(new int[] { 5, 2, 1, 4, 5, 5, 2 });
var olist = new OrderedList<int>(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<int>(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>();
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<int>(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<int> { 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<int>();
CollectionAssert.AreEqual(new int[0], test.WithinRange(6, 8));
test = new OrderedList<int>{ -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<ArgumentException>(() => test.WithinRange(6, 4));
}
}
答案 1 :(得分:8)
你找到了吗? Generic SortedList和SortedList。
所以我错过了重复部分,这让我更加努力。但这是我如何解决它:
var sortedList = new SortedList<LayerColor, SortedList<Layer, Layer>>();
var redSortedList = new SortedList<Layer, Layer>();
// Add all layers associated with the color red
sortedList.Add(LayerColor.Red, redSortedList);
这对你有用吗?此外,我更喜欢使用linq,但如果你真的想要一个排序列表,我的解决方案很可能会有效。
上次尝试:):
public class YourClass
{
private List<Layer> _layers;
public List<Layer> Layers
{
get
{
_layers = _layers.OrderBy(y => y.LayerColor).ThenBy(y => y.Name).ToList();
return _layers;
}
set
{
_layers = value;
}
}
}
请注意,我是直接在浏览器中编写而不是在VS中测试它(坐在OS X上),但你可能会明白这一点。
答案 2 :(得分:1)
你走在正确的轨道上。我将创建一个继承自Collection的自定义集合类。在此自定义集合中,您可以覆盖插入/删除方法,并在添加/删除项目时对集合进行排序。
答案 3 :(得分:1)
您可以使用常规List<T>
,但在显示列表之前调用Sort()
方法,并在添加新值之后调用{{1}}方法。这应该为您提供所需的功能。该应用程序的性能足够好。
当然,您必须定义自己的比较才能使用,但这不应该太麻烦。
如果您没有任何钩子进入可用于对列表进行排序的add事件,那么您可以将列表包装在@Justin推荐的自定义集合类中。
答案 4 :(得分:1)
使用System.Linq,执行:
from layer in layers
orderby layer.Color, layer.Name
select layer
答案 5 :(得分:1)
如果排序仅用于显示目的,请让WPF处理它:
ICollectionView view = CollectionViewSource.GetDefaultView(Layers);
view.SortDescriptions.Add(new SortDescription("Color", ListSortDirection.Ascending);
view.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending);
然后将Layers
绑定到您的用户界面ItemsControl
。
答案 6 :(得分:-2)
首先在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.
}
}
答案 7 :(得分:-2)
你可以使用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<Layer>().OrderBy(l => l.Color)
.ThenBy(l => l.Name);