在 C# 中寻找内置的有序列表

Reg*_*nni 3 c# list

我在 C# 中对列表进行排序时遇到问题。我提出的解决方案很笨拙,对我来说似乎有点黑客。我正在寻找一个优雅的解决方案来解决下面描述的问题。我想让排序保留在类中,以避免在代码的其他部分使用 .Sort() 或 .OrderBy() 。一个 9 岁的帖子表明不存在这样的类/解决方案。但是,我不禁感到有些类似于 OrderedList 的东西不是 IDictionary。

using System;
using System.Collections.Generic;

namespace ConsoleApp5
{
    class Program
    {
        static void Main(string[] args)
        {
                var obj = new Class1();
                obj.Test();
        }
    }

    public class Class1
    {
        public void Test()
        {
            var myList = new List<MyObject>();

            myList.Add(new MyObject { Id = 1, Name = "ZZZ" });
            myList.Add(new MyObject { Id = 2, Name = "NNN" });
            myList.Add(new MyObject { Id = 3, Name = "PPP" });
            myList.Add(new MyObject { Id = 4, Name = "AAA" });

            foreach (var obj in myList)
            {
                Console.WriteLine($"{obj.Name} -> {obj.Id}");
            }

        }

        public class MyObject
        {
            public int Id { get; set; }
            public string Name { get; set; }

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

并且列表按顺序打印出来:

ZZZ -> 1
NNN -> 2
PPP -> 3
AAA -> 4
Run Code Online (Sandbox Code Playgroud)

任何人都可以为将提供的有序列表建议一个合适的类:

AAA -> 4
NNN -> 2
PPP -> 3
ZZZ -> 1
Run Code Online (Sandbox Code Playgroud)

Jes*_*Wit 7

这里有多种选择:

对列表进行原地排序:

myList.Sort((x, y) => string.Compare(x.Name, y.Name));
Run Code Online (Sandbox Code Playgroud)

使用比较器对列表进行原地排序:

class MyObjectNameComparer : IComparer<MyObject>
{
    public int Compare(MyObject x, MyObject y)
    {
        return string.Compare(x.Name, y.Name);
    }
}

myList.Sort(new MyObjectNameComparer());
Run Code Online (Sandbox Code Playgroud)

将您的班级标记为IComparable<MyObject>,然后就地排序。这将影响以下行为Comparer<MyObject>.Default

class MyObject : IComparable<MyObject>
{
    public int Id { get; set; }
    public string Name { get; set; }

    public int CompareTo(MyObject other)
    {
        return string.Compare(this.Name, other.Name);
    }
}

// then
myList.Sort();
Run Code Online (Sandbox Code Playgroud)

使用 linq 以排序方式迭代,但保持列表原样:

foreach (var myObject in myList.OrderBy(x => x.Name))
{
    // ordered results
}
Run Code Online (Sandbox Code Playgroud)

使用 a SortedList,它有点像字典,但随后按键而不是哈希码进行排序。这意味着每个对象都需要唯一的“名称”(键):

var sortedList = new SortedList<string, MyObject>();
sortedList.Add("ZZZ", new MyObject { Id = 1, Name = "ZZZ" });
sortedList.Add("NNN", new MyObject { Id = 2, Name = "NNN" });
sortedList.Add("PPP", new MyObject { Id = 3, Name = "PPP" });
sortedList.Add("AAA", new MyObject { Id = 4, Name = "AAA" });
// Below would fail, because the key needs to be unique...
// sortedList.Add("AAA", new MyObject { Id = 5, Name = "AAA" });

foreach (var entry in sortedList)
{
    Console.WriteLine($"{entry.Value.Name} -> {entry.Value.Id}");
}
Run Code Online (Sandbox Code Playgroud)

或者使用 a SortedSet<T>,它维护有序的、唯一的项目(即你不能添加双打)。

var set = new SortedSet<MyObject>(new MyObjectNameComparer());
set.Add(new MyObject { Id = 1, Name = "ZZZ" });
// etc.
Run Code Online (Sandbox Code Playgroud)

使用有序枚举器创建自定义集合。这将使客户端以排序的方式迭代项目。此版本针对插入进行了“优化”:

class OrderedMyObjectCollection : ICollection<MyObject>
{
    private List<MyObject> innerList = new List<MyObject>();
    public int Count => this.innerList.Count;
    public bool IsReadOnly => false;
    public void Add(MyObject item) => this.innerList.Add(item);
    public void Clear() => this.innerList.Clear();
    public bool Contains(MyObject item) => this.innerList.Contains(item);

    public void CopyTo(MyObject[] array, int arrayIndex)
    {
        // Could be more efficient...
        this.ToList().CopyTo(array);
    }

    // Magic in the ordered enumerator.
    public IEnumerator<MyObject> GetEnumerator() => this.innerList.OrderBy(x => x.Name).GetEnumerator();
    public bool Remove(MyObject item) => this.innerList.Remove(item);
    IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
}
Run Code Online (Sandbox Code Playgroud)

如果您需要优化迭代,您实际上可以在添加时将项目存储在正确的索引处:

class OrderedList<T> : IList<T>
{
    // see above for the comparer implementation 
    private readonly IComparer<T> comparer;
    private readonly IList<T> innerList = new List<T>();

    public OrderedList()
        : this(Comparer<T>.Default)
    {
    }

    public OrderedList(IComparer<T> comparer)
    {
        this.comparer = comparer ?? throw new ArgumentNullException(nameof(comparer));
    }

    public T this[int index]
    {
        get => this.innerList[index];
        set => throw new NotSupportedException("Cannot set an indexed item in a sorted list.");
    }

    public int Count => this.innerList.Count;
    public bool IsReadOnly => false;

    // Magic in the insert
    public void Add(T item)
    {
        int index = innerList.BinarySearch(item, comparer);
        index = (index >= 0) ? index : ~index;
        innerList.Insert(index, item);
    }

    public void Clear() => this.innerList.Clear();
    public bool Contains(T item) => this.innerList.Contains(item);
    public void CopyTo(T[] array, int arrayIndex) => this.innerList.CopyTo(array);
    public IEnumerator<T> GetEnumerator() => this.innerList.GetEnumerator();
    public int IndexOf(T item) => this.innerList.IndexOf(item);
    public void Insert(int index, T item) => throw new NotSupportedException("Cannot insert an indexed item in a sorted list.");
    public bool Remove(T item) => this.innerList.Remove(item);
    public void RemoveAt(int index) => this.innerList.RemoveAt(index);
    IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
}
Run Code Online (Sandbox Code Playgroud)

  • 你缺少[SortedSet](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.sortedset-1?view=netframework-4.8) (2认同)