【问题标题】:Most efficient way of sorting ObservableCollection<T> with a new item in it使用新项目对 ObservableCollection<T> 进行排序的最有效方法
【发布时间】:2010-12-13 14:35:44
【问题描述】:

我的 ViewModel 类如下:

public class ListViewModel
{
    public ObservableCollection<InfoItem> List { get; set; }
}

public interface InfoItem
{
  int Reference { get; }
  string Name { get; }
}

集合按显示在 UI 中的名称排序。我有一个场景,其中集合包含几千个项目,我向集合中添加了一个新项目。

按名称对我的收藏重新排序以使新项目出现在列表中的正确位置的最有效方法是什么?

【问题讨论】:

    标签: c# wpf performance


    【解决方案1】:

    如果您的集合已经排序,则对其执行二分搜索以找出应在何处插入新项目,然后调用Insert。将项目添加到最后,然后使用整个集合将非常浪费。

    很遗憾IList&lt;T&gt; 上没有通用的BinarySearch 扩展方法,但编写起来应该不会太难。假设您想要编写一个通用方法来执行此操作(我建议您这样做 - 它不会比编写 InfoItem 特定的方法困难得多)您会要么想要拍摄IComparer&lt;T&gt;投影,例如

    public static int BinarySearch<T>(this IList<T> source, IComparer<T> comparer)
    

    public static int BinarySearch<TSource, TKey>(
        this IList<TSource> source,
        Func<TSource, TKey> keySelector)
    

    我建议你让返回值遵循List&lt;T&gt;.BinarySearch 的约定,这样如果没有找到匹配项,它会返回要插入项目的索引的按位否定。

    【讨论】:

      【解决方案2】:

      由于您的收藏已经排序,只需Insert 将新项目放在适当的位置,您可以通过binary search 发现该项目。不幸的是,IList&lt;T&gt; 上没有内置的二进制搜索,但您可以轻松地制作一个完成这项工作的扩展方法。执行二进制搜索时要小心,不要引入classic bug(该错误是在计算lowhigh 索引的平均值时可能溢出,因为low + high 可能会溢出)。您可以使用List&lt;T&gt;.BinarySearch 作为模板。

      【讨论】:

      【解决方案3】:

      关注@JonSkeet 的回答,我做了IList 的BinarySearch 扩展,类似于.NET sourcecode

      public static class IListBinarySearchHelper {
          public static int BinarySearch<T>(this IList<T> source, int index, int count, T item, IComparer<T> comparer) {
              if (index < 0) throw new Exception("Need non negative number of index.");
              if (count < 0) throw new Exception("Need non negative number of count.");
              if (source.Count - index < count) throw new Exception("Invalid offset length of count.");
              Contract.Ensures(Contract.Result<int>() <= index + count);
              Contract.EndContractBlock();
      
              return Array.BinarySearch<T>(source.Cast<T>().ToArray(), index, count, item, comparer);
          }
      
          public static int BinarySearch<T>(this IList<T> source, T item) {
              Contract.Ensures(Contract.Result<int>() <= source.Count);
              return BinarySearch(source, 0, source.Count, item, null);
          }
      
          public static int BinarySearch<T>(this IList<T> source, T item, IComparer<T> comparer) {
              Contract.Ensures(Contract.Result<int>() <= source.Count);
              return BinarySearch(source, 0, source.Count, item, comparer);
          }
      }
      

      所以现在我们可以这样做了:

      var nums = new ObservableCollection<int>();
      nums.Add(1);
      nums.Add(3);
      nums.Add(8);
      
      var result = nums.BinarySearch(1);   /// return 0
      result = nums.BinarySearch(4);       /// return -3
      

      这对我来说很完美。

      返回值与Array.BinarySearch相同,正数表示找到的索引,负数表示插入点的提示。

      按名称重新排序我的收藏的最有效方法是什么 新项目是否出现在列表中的正确位置?

      要回答这个问题,只需在与 BinarySearch 返回值相关的点插入值即可。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-02-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-12-23
        • 2010-11-07
        • 1970-01-01
        相关资源
        最近更新 更多