【问题标题】:Why is there no Sort for IList<T>?!?! (edited)为什么 IList<T> 没有排序?!?! (已编辑)
【发布时间】:2009-07-20 02:50:00
【问题描述】:

当我发现没有直接的方法可以对 IList 进行排序或执行二进制搜索时,我感到非常惊讶。就像有静态方法可以对数组进行排序和执行二进制搜索一样,我认为拥有采用 IList 的类似静态方法会非常有帮助。

目前:

class Array
{
    static Sort<T>(T[] array);
    static int BinarySearch<T>(T[] array, T item);
}

我希望他们能补充:

class List
{
    static Sort<T>(IList<T> list);
    static int BinarySearch<T>(IList<T> list, T item);
}

我看了一眼 .NET Framework 4.0 Beta SDK,发现仍然似乎无法解决这个问题。

我知道我可以通过创建一个扩展方法来解决这个问题,该方法检查它是否是 List,然后使用 List 实例进行排序/搜索;但是,如果它不是 List 的实例,那么我必须执行一个副本(这对于非常大的列表来说很糟糕)。我知道我可以做到这一切,但为什么呢?他们故意省略此功能是否有某些原因?

为了尝试在 .NET 4.0 框架中实现这一点,我通过 Microsoft 的 Connect 程序创建了一个建议。如果您像我一样对这个问题感到沮丧,请投票,也许它会被添加。

https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=474201

【问题讨论】:

  • 您能否将其改写为一个问题,也许是“C# 是否有任何理由没有对 IList 的内置排序和二进制搜索?”现在你只是在发表声明并呼吁人们在微软的网站上投票支持这个,这冒着被作为垃圾邮件关闭的风险
  • 它有点隐藏在杂乱无章中,但有一个问题可以帮助我理解如果微软故意忽略它为什么不存在:“他们故意省略有什么原因吗?这个功能?”
  • 井堆栈溢出是一个问答网站。把更多的注意力放在你的问题上是一种很好的礼仪(尤其是在问题的标题中)。现在听起来您已经假设这是一个错误,并且您希望其他人告诉微软您同意。更好的 SO 精神是明确问题是“这是有充分的理由还是这是一个错误?”
  • @Kip:我把标题改成了一个问题。感谢您的建议。

标签: c# .net c#-4.0


【解决方案1】:

LINQ 有一个适用于所有 IEnumerable 的 OrderBy 方法,包括 IList。您可以使用 OrderBy 完成同样的事情。

// Order a list of addresses:
IList<string> list = ...
var orderedList = list.OrderBy(input => input);

【讨论】:

  • 你知道他们是如何在幕后做到这一点的吗?我假设由于枚举器通常只在需要时才被评估(惰性评估),所以会使用像选择排序这样的东西,因为它是 O(n^2)。
  • @dewald - 我认为一般规则是 LINQ-to-Objects 运算符是“适合大多数用途的懒惰”。鉴于某些 IEnumerable 实现可能是惰性的,让 OrderBy 反复枚举输入以查找连续较低的值将非常浪费。也就是说,如果我没看错的话,它会在幕后使用 EnumerableSorter.QuickSort,即。基于数组的快速排序实现。
  • 如果您关心排序是立即排序还是惰性排序,那么请使用 list.OrderBy(...) 代替 list.OrderBy(...).ToList(),这将强制排序立即进行。
  • @Kyralessa - 这是一个绕过惰性评估的好建议,但是,它并没有解决我最初担心必须创建 ILIst 的副本才能能够排序。
  • “你知道他们是如何在幕后做到这一点的吗?” 他们可能分配了一个迭代器类,并使用你传入的 orderby 函数返回比较的项目。跨度>
【解决方案2】:

我认为不包括IList&lt;T&gt; 的排序方法是一个很好的例子。首先,它会给那些想要实现 IList 的人带来额外的复杂性,其次它会使 IList 接口更难符合Interface Segregation Principle

如果我需要对IList&lt;T&gt; 执行排序,通常我会创建一个新的List&lt;T&gt; 并将IList&lt;T&gt; 作为参数传入

例如:

        public IList<Address> SortAddresses(IList<Address> addresses)
        {
            var sortedAddresses = new List<Address>(addresses);
            sortedAddresses.Sort();
            return sortedAddresses;
        }

【讨论】:

  • 但是,重要的是要意识到这不会对列表进行适当的排序(这可能不是问题)。
  • @lomaxx:它会如何为那些实现 IList 的类增加复杂性?索引 get/set 属性已经存在,可以简单地在静态 Sort(List) 方法中使用。
  • @dewald:因为要写更多的代码,而且对每种类型的集合进行排序没有意义。
  • 对不起,不是 C# 人...如果已经有一个静态的 Sort(List),那么我肯定同意不需要在界面中添加排序功能。 Java 类似:Collections.sort(List>) 是任何列表的静态排序方法。
  • 他不是要求接口上的排序成员函数,而是其他地方的静态函数,以 IList 作为参数。这不会增加实现 IList 的复杂性。
【解决方案3】:

好消息是你可以很容易地编写这样的方法;并且感谢 C# 3.0 扩展方法,您可以在接口上实现这一点:

public static class ListExt
{
    public static void AddRange<T>(this IList<T> list, IEnumerable<T> items) {
        foreach(T item in items) list.Add(item);
    }
    public static void Sort<T>(this IList<T> list) {
        Sort<T>(list,Comparer<T>.Default); // ordinal sort by default
    }
    public static void Sort<T>(this IList<T> list, IComparer<T> comparer)
    { // very poor implementation!
        List<T> concreteList = new List<T>(list);
        concreteList.Sort(comparer); // cheat!
        list.Clear();
        list.AddRange(concreteList);
    }
    public static int BinarySearch<T>(this IList<T> list, T item) {
        return BinarySearch<T>(list, item, Comparer<T>.Default);
    }
    public static int BinarySearch<T>(this IList<T> list, T item,
        IComparer<T> comparer)
    {...} // TODO
}

现在剩下的就是 TODO 代码本身(并且可能重写 Sort ;-p);但在那之后:

IList<T> list = ...
list.Sort();
T huntFor = ...
int i = list.BinarySearch(huntFor);

重要的是,IList&lt;T&gt; 有一个读/写索引器,因此当然可以在没有上述 hack 的情况下同时进行排序和二进制搜索。

【讨论】:

  • @Marc:这些都是很好的扩展方法,但是,我很惊讶我们必须编写代码来执行这些基本操作。就像你说的,“IList 有一个读/写索引器,所以当然可以在没有上述技巧的情况下同时进行排序和二进制搜索”。在我看来,这些方法应该是框架的一部分,以防止每个项目都必须创建自己的版本。
【解决方案4】:

您确实有ArrayList.Adapter 允许使用ArrayList 的排序例程,但它会对未装箱值类型的通用列表造成巨大的性能影响,以及虚拟调用和接口调度开销。

对于引用类型和值类型,接口调度可能很昂贵,这意味着调用 ICollection&lt;T&gt;.CopyTo 一个数组 T[] 然后单独排序可能是最快的通用选项,包括直接在IList&lt;T&gt; 对象。

List&lt;T&gt; 有一个Sort 方法,因为它可以非常高效地操作T[] 类型的底层数组。对于任意的IList&lt;T&gt;,根本没有办法做到这一点。

【讨论】:

【解决方案5】:

我不是 C# 专家,但很少有 List 实现支持排序、二分查找甚至索引检索。列表通常基于linked lists,通常不提供O(1) 通过索引检索项目的方式。如果您想快速定位一个元素,那么请使用诸如tree 或其他人建议的数组等使元素保持排序的东西。

我确实发现IList 包含indexed retrieval method 的事实很有趣。您可能需要考虑使用SortedList 而不是List,因为它看起来应该支持在O(1) 时间按索引或键进行查找。一般来说,如果您需要支持快速查找的东西,那么请寻找一种保持其元素有序而不是显式排序的数据结构。如果不出意外,C# 已经拥有相当多的数据结构了。

【讨论】:

  • 在 C# 中,“列表”通常在“集合”之上提供直接元素索引,即简单地添加/删除元素。
  • 您可以使用归并排序在 O(nlgn) 中对链表进行排序。
  • 我实际上并不知道使用链表作为存储的IList&lt;T&gt; 的任何实现。特别是,stock LinkedList&lt;T&gt;实现IList&lt;T&gt;
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-10
  • 1970-01-01
  • 2023-03-29
  • 1970-01-01
相关资源
最近更新 更多