【问题标题】:IndexOf too slow on list. Faster solution?列表中的 IndexOf 太慢。更快的解决方案?
【发布时间】:2009-07-02 17:40:37
【问题描述】:

我有一个必须是保留顺序的通用列表,以便我可以检索列表中对象的索引。问题是 IndexOf 太慢了。如果我将 IndexOf 注释掉,则代码会尽可能快地运行。有没有更好的方法,比如 preserved 用于 c# 的有序哈希列表?

谢谢, 内特

  • 编辑 - 添加/插入项目的顺序是它需要的顺序。不需要对它们进行排序。此列表也有可能经常更新、添加、删除、插入。基本上我需要将对象转换为索引,因为它们在网格控件中表示,因此我可以根据索引对网格控件执行操作。

【问题讨论】:

  • 代码?你在列表中存储什么?顺序重要吗?
  • 您是要检索对象本身还是仅检索对象的索引?
  • 关于你在做什么的更多细节?
  • 您能说得更具体些吗?你在做什么让 indexOf 这么慢?您是否希望针对查找进行优化?您对插入/删除的速度要求是什么?您需要连续的项目集合吗?你能用 HashSet 代替吗?
  • Ordered表示已排序,还是表示必须保留顺序?

标签: c# list performance indexof


【解决方案1】:

如果它没有排序,但顺序需要保留,那么您可以有一个单独的Dictionary<YourClass, int>,其中包含每个元素的索引。

如果您想要排序列表,请查看以前的帖子 - 您可以在 .Net 3.5 中使用 SortedList<Tkey, TValue>,或者在较旧的 .Net 版本中对其进行排序并使用 BinarySearch。

[编辑] 你可以在网上找到类似的例子,例如:OrderedList。这个内部使用了 ArrayList 和 HashTable,但您可以轻松地将其设为通用。

[Edit2] 哎呀..我给你的​​例子没有实现我开始描述的 IndexOf 方式......但你明白了 - 一个列表应该是有序的,另一个用于快速查找。

【讨论】:

  • 我考虑过使用字典的方法。我不喜欢它的事情是必须更新插入/删除之后的字典中的每个条目。但我还没有排除这种可能性。
  • 在插入/删除过程中,如果不影响一点速度,您就无法加快 IndexOf...
  • 我最终做了什么,并且性能提升非常好,通过将对象添加到字典中作为键和索引作为值来记住列表中对象的索引 IndexOf被称为。这样,如果再次调用它,它就可以在字典中查找值。如果列表发生变化,我只需清除字典即可。另一个答案与下面的类似,但我会将这个标记为答案,因为它有更多的选票。
  • @NastyNateDoggy:这听起来像是最好的解决方案 - 延迟缓存会降低内存使用量。
【解决方案2】:

使用List<T>.Sort对其进行排序,然后使用List<T>.BinarySearch方法:"在整个排序后的List(T)中搜索一个元素[...]这个方法是一个O(log n)操作,其中n是范围内的元素个数。"

【讨论】:

    【解决方案3】:

    见底部this article here

    看来,编写自己的方法来检索索引比使用 IndexOf 方法快得多,因为它会根据类型调用虚拟方法。

    因此,这样的事情可能会提高您的表现。我编写了一个小型单元测试来验证这是否可以提高搜索性能,并且确实提高了 10,000 个项目的列表中的大约 15 倍。

    static int GetIndex(IList<Item> list, Item value)
    {
        for (int index = 0; index < list.Count; index++)
        {
            if (list[index] == value)
            {
                 return index;
            } 
        }
        return -1;
    }
    

    【讨论】:

    • 该代码如何使用?我得到“找不到类型或命名空间名称‘Item’。
    • @JohnSaps 您应该将“Item”替换为列表中存储的项目类型。因此,如果您有“IList”,例如,您可以将“Item”替换为“Contact”。
    【解决方案4】:

    也许您正在寻找SortedList&lt;TKey, TValue&gt;

    【讨论】:

    • 我相信他的意思只是“保留顺序”,而不是排序。
    【解决方案5】:

    如果列表中对象的顺序必须保留,那么我能想到的唯一方法是告诉对象它的索引是什么位置是将其添加到列表中的时间。这样您就可以查询对象以获取其在列表中的索引。缺点,在我看来也是一个很大的缺点,是插入的对象现在依赖于列表。

    【讨论】:

    • 感谢您的回复。我也考虑过这一点,但选择使用我在已接受答案下的评论中描述的内容。
    【解决方案6】:

    如果您需要对项目进行排序,我建议使用SortedList&lt;TKey, TValue&gt;SortedDictionary&lt;TKey, TValue&gt; 类。区别如下。

    • SortedList&lt;TKey, TValue&gt; 使用的内存少于SortedDictionary&lt;TKey, TValue&gt;

    • SortedDictionary&lt;TKey, TValue&gt; 具有更快的插入和删除操作 未排序的数据:O(log n) 而不是 SortedList&lt;TKey, TValue&gt; 的 O(n)。

    • 如果列表是从排序的数据中一次性填充的,SortedList&lt;TKey, TValue&gt; 是 比SortedDictionary&lt;TKey, TValue&gt; 快。

    如果您只想保留顺序,您可以使用Dictionary&lt;TKey, TValue&gt; 并将项目存储为键,将索引存储为值。缺点是对项目进行重新排序、插入或删除非常昂贵。

    【讨论】:

    • 是的,字典是我考虑过的一种方法,但会增加一定程度的维护和性能损失。
    【解决方案7】:

    好吧,您没有理由必须订购哈希列表……这就是重点。但是,哈希列表应该很容易做到这一点。

    【讨论】:

      【解决方案8】:

      如果您使用的是 List 类,那么您可以在最初填充后使用 Sort 方法对其进行排序,然后使用 BinarySearch 方法查找合适的元素。

      【讨论】:

        【解决方案9】:

        我不确定 C# 中的细节,但您可以对其进行排序(QuickSort?),然后对其使用二进制搜索(BinarySearch 性能为 O(log2(N)),而不是 Sequential,例如indexOf,即 O(n))。 (重要提示:对于二分搜索,必须对结构进行排序)

        当您将项目插入数据结构时,您也可以尝试修改后的二进制搜索来查找插入点,或者如果您要添加一个大组,您可以添加它们然后对它们进行排序。

        唯一的问题是插入会比较慢。

        【讨论】:

          猜你喜欢
          • 2016-03-18
          • 1970-01-01
          • 1970-01-01
          • 2013-07-30
          • 2013-11-19
          • 1970-01-01
          • 1970-01-01
          • 2017-09-10
          • 1970-01-01
          相关资源
          最近更新 更多