【问题标题】:What .NET collection provides the fastest search什么 .NET 集合提供最快的搜索
【发布时间】:2010-11-03 19:00:54
【问题描述】:

我有 60k 项需要对照 20k 查找列表进行检查。是否有一个集合对象(如ListHashTable)提供了一个非常快的Contains() 方法?还是我必须自己写?换句话说,默认的Contains() 方法是只扫描每个项目还是使用更好的搜索算法。

foreach (Record item in LargeCollection)
{
    if (LookupCollection.Contains(item.Key))
    {
       // Do something
    }
}

注意。查找列表已排序。

【问题讨论】:

  • Contains for List 不适用于对象列表,因为它正在比较引用。
  • 排序数据?二进制搜索 - 请参阅@Mark 的答案。
  • 根据我的经验,HashtTable 胜过最多 200 万个项目
  • 顺便说一句,如果您的元素的顺序有意义并且分布相当均匀,您可以通过让您的第一次猜测在您的项目的估计范围内更快地进行二分搜索。这可能对您的特定应用有任何意义,也可能没有任何意义。
  • 如果您想简化这些内容但避免使用哈希集,请不要忘记 System.Collections.Generic.SortedList(TKey, TValue)。

标签: c# .net search collections


【解决方案1】:

在最一般的情况下,将System.Collections.Generic.HashSet 视为您的默认“包含”主力数据结构,因为评估Contains 需要恒定的时间。

“什么是最快的可搜索集合”的实际答案取决于您的具体数据大小、有序性、散列成本和搜索频率。

【讨论】:

  • 注意:不要忘记重写 hashcode 函数。为了提高性能,请在构造函数中预先生成哈希码。
  • @Brian:好点。我(毫无根据地)假设 Record.Key 是某种内置类型。
  • @Brian:比起预生成,我更喜欢第一次存储生成的,为什么要用你不知道它是否会被使用的东西来减慢构造函数的速度?
  • 仅供参考:性能测试 - 我为字符串创建了 List 和 HashSet 之间的比较。我发现 HashSet 比 List 快 1000 倍左右。
  • @Quango:3 年后,但实际上,如果您不指定数据集的大小,则此性能比较毫无意义:哈希集具有 O(1) 搜索,列表具有 O(n) 搜索, 所以性能比与 n 成正比。
【解决方案2】:

如果您不需要订购,请尝试HashSet<Record>(.Net 3.5 的新手)

如果您这样做,请使用List<Record> 并致电BinarySearch

【讨论】:

  • 或者,在 .NET >= 4 中,使用 SortedSet
  • 或者更好的是,来自 System.ImmutableCollections 的ImmutableSortedSet
【解决方案3】:

你考虑过List.BinarySearch(item)吗?

您说您的大量收藏已经排序,所以这似乎是一个绝佳的机会?哈希肯定是最快的,但这会带来自己的问题,并且需要更多的存储开销。

【讨论】:

  • 你说得对,当使用可变对象作为键时,哈希可能会带来一些不良问题。
【解决方案4】:

如果您不担心每一个最后一点的性能都会受到影响,那么使用 HashSet 或二分搜索的建议是可靠的。您的数据集不够大,这在 99% 的情况下都会成为问题。

但是,如果这只是您要执行此操作的数千次之一,并且性能至关重要(并且使用 HashSet/二分搜索证明是不可接受的),您当然可以编写自己的算法来遍历排序列表进行比较你去了。每个列表最多会被遍历一次,并且在病理情况下不会很糟糕(一旦你走这条路,你可能会发现比较,假设它是一个字符串或其他非整数值,将是真正的费用和下一步就是优化)。

【讨论】:

    【解决方案5】:

    如果可以对您的项目进行排序,那么有一种比在哈希表或 b 树中进行键查找更快的方法。尽管如果您的项目不可排序,则无论如何您都不能真正将它们放入 b-tree 中。

    无论如何,如果对两个列表都进行排序,那么只需按顺序遍历查找列表即可。

    Walk lookup list
       While items in check list <= lookup list item
         if check list item = lookup list item do something
       Move to next lookup list item
    

    【讨论】:

    • 是的,确实如此。如果你有两个排序列表,你只需要遍历一次。
    【解决方案6】:

    如果您使用的是 .Net 3.5,您可以使用以下方法制作更简洁的代码:

    foreach (Record item in LookupCollection.Intersect(LargeCollection))
    {
      //dostuff
    }
    

    我这里没有 .Net 3.5,因此未经测试。它依赖于扩展方法。并不是说LookupCollection.Intersect(LargeCollection) 可能与LargeCollection.Intersect(LookupCollection) 不一样……后者可能要慢得多。

    这假设 LookupCollection 是 HashSet

    【讨论】:

      【解决方案7】:

      保持列表 x 和 y 的排序顺序。

      如果 x = y,执行你的操作,如果 x

      这个路口的运行时间与min成正比(大小(x),大小(y))

      不要运行 .Contains () 循环,这与 x * y 成正比,更糟。

      【讨论】:

      • +1 用于更高效的算法。即使列表当前未排序,首先对它们进行排序然后运行此算法会更有效。
      • 在最坏的情况下,运行时间不会与 max(size(x),size(y)) 成正比吗?示例:int[] x = {99,100}; int[] y = {0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
      • 否,因为一旦完成较小的集合,您就可以附加较大集合中的剩余元素,因为它们已经排序。我认为这个过程类似于 Merge Sort。
      【解决方案8】:

      您应该阅读this blog,它使用单线程和多线程技术对几种不同类型的集合和方法进行了速度测试。

      根据结果,List 和 SortedList 上的 BinarySearch 是表现最好的,在将某物作为“价值”查找时经常并驾齐驱。

      当使用允许“键”的集合时,Dictionary、ConcurrentDictionary、Hashset 和 HashTables 的整体表现最好。

      【讨论】:

        【解决方案9】:

        我已经做了一个测试:

        • 前 - 3 个字符,包含 A-Z0-9 的所有可能组合
        • 用这些字符串填充此处提到的每个集合
        • 最后 - 为每个集合搜索并计时一个随机字符串(每个集合使用相同的字符串)。

        此测试在保证有结果时模拟查找。

        然后我将初始集合从所有可能的组合更改为仅 10,000 个随机 3 字符组合,这应该会导致随机 3 字符查找的 4.6 命中率中的 1,因此这是一个不能保证的测试结果,并再次运行测试:

        恕我直言,哈希表虽然最快,但并不总是最方便的;处理对象。但是 HashSet 离它如此之近,可能是值得推荐的。

        只是为了好玩(你知道有趣)我运行了 168 万行(4 个字符):

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-10-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-02-18
          • 2013-02-17
          相关资源
          最近更新 更多