当前位置:  开发笔记 > 编程语言 > 正文

HashSet与List性能

如何解决《HashSet与List性能》经验,为你挑选了8个好方法。

很明显,泛型HashSet类的搜索性能高于泛型List类.只需将基于散列的密钥与线性方法进行比较即可List.

但是,计算散列键本身可能需要一些CPU周期,因此对于少量项目,线性搜索可以是一个真正的替代HashSet.

我的问题:收支平衡在哪里?

为了简化场景(并且公平),我们假设List该类使用元素的Equals()方法来标识项目.



1> innominate22..:

很多人都说,一旦你达到速度实际上是一个HashSet总是会打败的关注的大小List,但这取决于你在做什么.

假设你有一个List平均只有5个项目.在大量循环中,如果每个循环添加或删除单个项目,您最好使用a List.

我在我的机器上对此进行了测试,而且,它必须非常小才能获得优势List.对于短字符串列表,对于大小为20之后的对象,优势在大小5之后消失.

1 item LIST strs time: 617ms
1 item HASHSET strs time: 1332ms

2 item LIST strs time: 781ms
2 item HASHSET strs time: 1354ms

3 item LIST strs time: 950ms
3 item HASHSET strs time: 1405ms

4 item LIST strs time: 1126ms
4 item HASHSET strs time: 1441ms

5 item LIST strs time: 1370ms
5 item HASHSET strs time: 1452ms

6 item LIST strs time: 1481ms
6 item HASHSET strs time: 1418ms

7 item LIST strs time: 1581ms
7 item HASHSET strs time: 1464ms

8 item LIST strs time: 1726ms
8 item HASHSET strs time: 1398ms

9 item LIST strs time: 1901ms
9 item HASHSET strs time: 1433ms

1 item LIST objs time: 614ms
1 item HASHSET objs time: 1993ms

4 item LIST objs time: 837ms
4 item HASHSET objs time: 1914ms

7 item LIST objs time: 1070ms
7 item HASHSET objs time: 1900ms

10 item LIST objs time: 1267ms
10 item HASHSET objs time: 1904ms

13 item LIST objs time: 1494ms
13 item HASHSET objs time: 1893ms

16 item LIST objs time: 1695ms
16 item HASHSET objs time: 1879ms

19 item LIST objs time: 1902ms
19 item HASHSET objs time: 1950ms

22 item LIST objs time: 2136ms
22 item HASHSET objs time: 1893ms

25 item LIST objs time: 2357ms
25 item HASHSET objs time: 1826ms

28 item LIST objs time: 2555ms
28 item HASHSET objs time: 1865ms

31 item LIST objs time: 2755ms
31 item HASHSET objs time: 1963ms

34 item LIST objs time: 3025ms
34 item HASHSET objs time: 1874ms

37 item LIST objs time: 3195ms
37 item HASHSET objs time: 1958ms

40 item LIST objs time: 3401ms
40 item HASHSET objs time: 1855ms

43 item LIST objs time: 3618ms
43 item HASHSET objs time: 1869ms

46 item LIST objs time: 3883ms
46 item HASHSET objs time: 2046ms

49 item LIST objs time: 4218ms
49 item HASHSET objs time: 1873ms

这是以图形显示的数据:

在此输入图像描述

这是代码:

static void Main(string[] args)
{
    int times = 10000000;


    for (int listSize = 1; listSize < 10; listSize++)
    {
        List list = new List();
        HashSet hashset = new HashSet();

        for (int i = 0; i < listSize; i++)
        {
            list.Add("string" + i.ToString());
            hashset.Add("string" + i.ToString());
        }

        Stopwatch timer = new Stopwatch();
        timer.Start();
        for (int i = 0; i < times; i++)
        {
            list.Remove("string0");
            list.Add("string0");
        }
        timer.Stop();
        Console.WriteLine(listSize.ToString() + " item LIST strs time: " + timer.ElapsedMilliseconds.ToString() + "ms");


        timer = new Stopwatch();
        timer.Start();
        for (int i = 0; i < times; i++)
        {
            hashset.Remove("string0");
            hashset.Add("string0");
        }
        timer.Stop();
        Console.WriteLine(listSize.ToString() + " item HASHSET strs time: " + timer.ElapsedMilliseconds.ToString() + "ms");
        Console.WriteLine();
    }


    for (int listSize = 1; listSize < 50; listSize+=3)
    {
        List list = new List();
        HashSet hashset = new HashSet();

        for (int i = 0; i < listSize; i++)
        {
            list.Add(new object());
            hashset.Add(new object());
        }

        object objToAddRem = list[0];

        Stopwatch timer = new Stopwatch();
        timer.Start();
        for (int i = 0; i < times; i++)
        {
            list.Remove(objToAddRem);
            list.Add(objToAddRem);
        }
        timer.Stop();
        Console.WriteLine(listSize.ToString() + " item LIST objs time: " + timer.ElapsedMilliseconds.ToString() + "ms");



        timer = new Stopwatch();
        timer.Start();
        for (int i = 0; i < times; i++)
        {
            hashset.Remove(objToAddRem);
            hashset.Add(objToAddRem);
        }
        timer.Stop();
        Console.WriteLine(listSize.ToString() + " item HASHSET objs time: " + timer.ElapsedMilliseconds.ToString() + "ms");
        Console.WriteLine();
    }

    Console.ReadLine();
}


尽管这个答案是完整的,但它无法回答有关列表与hashset搜索性能的原始问题.您正在测试插入和移除它们的速度,这比搜索需要更多的时间和不同的性能特征.再次尝试使用.Contains,您的图表将发生显着变化.
.NET框架中实际上有一个集合,它根据它包含的项目数在列表和hastable实现之间切换:[HybridDictionary](http://msdn.microsoft.com/en-us/library/system.collections .specialized.hybriddictionary.aspx).
非常感谢!这是一个很好的解释,我正在寻找能够比游戏引擎的"List "更快地添加和删除的东西,并且因为我通常会有大量的对象,所以这种集合将是完美的.
MS似乎已经放弃了它,因为它只有非通用版本.
@hypehuman CPU无法直接处理系统内存中的数据,而是将数据从内存中提取到其缓存中以进行处理.要移动的内存和实际到达的内存之间存在明显的延迟,因此CPU通常会请求一大块连续内存一次移动.这背后的想法是下一条指令所需的内存可能非常接近前一条指令使用的内存,因此通常已经在缓存中.当您的数据散布在整个内存中时,获得幸运的机会就会减少.
您添加和删除"string0"而不是"string"+ i.ToString())的原因是什么?

2> Eloff..:

你看错了.是的,List的线性搜索将击败HashSet以获得少量项目.但是性能差异通常对于小的集合无关紧要.这通常是您需要担心的大型系列,而这就是您对Big-O的看法.但是,如果你已经测量了HashSet性能的真正瓶颈,那么你可以尝试创建一个混合的List/HashSet,但是你可以通过进行大量的经验性能测试来做到这一点 - 而不是在SO上提问.


如果你有一个在循环中被多次击中的小集合怎么办?这不是一个不寻常的情况.
不,你会看到相当于几百个元素的相当大的性能差异.如果您正在进行HashSet擅长的访问类型(例如,集合中的元素X),那么总是使用HashSet.如果您的集合太小而List更快,那么这些查找非常罕见实际上是你的应用程序的瓶颈.如果你可以测量它是一个,那么你可以尝试优化它 - 但否则你就是在浪费你的时间.
*您需要担心的大型收藏品*.我们可以用以下术语重新定义这个问题:当小集合变得足够大以至于担心HashSet与List?数十,数万,数十亿元素?
@ om-nom-nom - 我认为重点在于引爆点在哪里无关紧要,因为:"如果性能令人担忧,请使用`HashSet `.在小数字的情况下`List `可能会更快,差异微不足道."

3> nawfal..:

比较两个表现不同的性能结构基本上没有意义.使用传达意图的结构.即使你说你List不会有重复和迭代顺序无关紧要使它与a相比HashSet,它仍然是一个糟糕的选择,List因为它相对较少容错.

也就是说,我将检查性能的其他方面,

+------------+--------+-------------+-----------+----------+----------+-----------+
| Collection | Random | Containment | Insertion | Addition |  Removal | Memory    |
|            | access |             |           |          |          |           |
+------------+--------+-------------+-----------+----------+----------+-----------+
| List    | O(1)   | O(n)        | O(n)      | O(1)*    | O(n)     | Lesser    |
| HashSet | O(n)   | O(1)        | n/a       | O(1)     | O(1)     | Greater** |
+------------+--------+-------------+-----------+----------+----------+-----------+

* Even though addition is O(1) in both cases, it will be relatively slower in HashSet since it involves cost of precomputing hash code before storing it.

** The superior scalability of HashSet has a memory cost. Every entry is stored as a new object along with its hash code. This article might give you an idea.


我的问题(六年前)并不是关于_theoretical_ performance.

4> core..:

是否使用HashSet <>或List <>归结为您需要如何访问您的集合.如果您需要保证物品的顺序,请使用列表.如果不这样做,请使用HashSet.让微软担心其散列算法和对象的实现.

HashSet将访问项而不必枚举集合(O(1)或其附近的复杂性),并且因为List保证顺序,与HashSet不同,必须枚举一些项(O(n)的复杂性).


.如果您需要保证物品的顺序,请使用列表.如果不这样做,请使用HashSet.让微软担心其散列算法和对象的实现.

5> drzaus..:

只是想我会为不同的场景编写一些基准来说明以前的答案:

    一些(12 - 20)个小字符串(长度在5到10个字符之间)

    许多(~10K)小弦

    一些长字符串(长度在200到1000个字符之间)

    许多(~5K)长串

    几个整数

    许多(~10K)整数

对于每个场景,查找出现的值:

    在列表的开头("开始",索引0)

    在列表的开头附近("早期",索引1)

    在列表的中间("中间",索引计数/ 2)

    接近列表末尾("迟到",索引计数-2)

    在列表的末尾("结束",索引计数-1)

在每个场景之前,我生成随机大小的随机字符串列表,然后将每个列表提供给一个哈希集.每个场景运行10,000次,主要是:

(测试伪代码)

stopwatch.start
for X times
    exists = list.Contains(lookup);
stopwatch.stop

stopwatch.start
for X times
    exists = hashset.Contains(lookup);
stopwatch.stop

样本输出

在Windows 7上测试,12GB Ram,64位,Xeon 2.8GHz

---------- Testing few small strings ------------
Sample items: (16 total)
vgnwaloqf diwfpxbv tdcdc grfch icsjwk
...

Benchmarks:
1: hashset: late -- 100.00 % -- [Elapsed: 0.0018398 sec]
2: hashset: middle -- 104.19 % -- [Elapsed: 0.0019169 sec]
3: hashset: end -- 108.21 % -- [Elapsed: 0.0019908 sec]
4: list: early -- 144.62 % -- [Elapsed: 0.0026607 sec]
5: hashset: start -- 174.32 % -- [Elapsed: 0.0032071 sec]
6: list: middle -- 187.72 % -- [Elapsed: 0.0034536 sec]
7: list: late -- 192.66 % -- [Elapsed: 0.0035446 sec]
8: list: end -- 215.42 % -- [Elapsed: 0.0039633 sec]
9: hashset: early -- 217.95 % -- [Elapsed: 0.0040098 sec]
10: list: start -- 576.55 % -- [Elapsed: 0.0106073 sec]


---------- Testing many small strings ------------
Sample items: (10346 total)
dmnowa yshtrxorj vthjk okrxegip vwpoltck
...

Benchmarks:
1: hashset: end -- 100.00 % -- [Elapsed: 0.0017443 sec]
2: hashset: late -- 102.91 % -- [Elapsed: 0.0017951 sec]
3: hashset: middle -- 106.23 % -- [Elapsed: 0.0018529 sec]
4: list: early -- 107.49 % -- [Elapsed: 0.0018749 sec]
5: list: start -- 126.23 % -- [Elapsed: 0.0022018 sec]
6: hashset: early -- 134.11 % -- [Elapsed: 0.0023393 sec]
7: hashset: start -- 372.09 % -- [Elapsed: 0.0064903 sec]
8: list: middle -- 48,593.79 % -- [Elapsed: 0.8476214 sec]
9: list: end -- 99,020.73 % -- [Elapsed: 1.7272186 sec]
10: list: late -- 99,089.36 % -- [Elapsed: 1.7284155 sec]


---------- Testing few long strings ------------
Sample items: (19 total)
hidfymjyjtffcjmlcaoivbylakmqgoiowbgxpyhnrreodxyleehkhsofjqenyrrtlphbcnvdrbqdvji...
...

Benchmarks:
1: list: early -- 100.00 % -- [Elapsed: 0.0018266 sec]
2: list: start -- 115.76 % -- [Elapsed: 0.0021144 sec]
3: list: middle -- 143.44 % -- [Elapsed: 0.0026201 sec]
4: list: late -- 190.05 % -- [Elapsed: 0.0034715 sec]
5: list: end -- 193.78 % -- [Elapsed: 0.0035395 sec]
6: hashset: early -- 215.00 % -- [Elapsed: 0.0039271 sec]
7: hashset: end -- 248.47 % -- [Elapsed: 0.0045386 sec]
8: hashset: start -- 298.04 % -- [Elapsed: 0.005444 sec]
9: hashset: middle -- 325.63 % -- [Elapsed: 0.005948 sec]
10: hashset: late -- 431.62 % -- [Elapsed: 0.0078839 sec]


---------- Testing many long strings ------------
Sample items: (5000 total)
yrpjccgxjbketcpmnvyqvghhlnjblhgimybdygumtijtrwaromwrajlsjhxoselbucqualmhbmwnvnpnm
...

Benchmarks:
1: list: early -- 100.00 % -- [Elapsed: 0.0016211 sec]
2: list: start -- 132.73 % -- [Elapsed: 0.0021517 sec]
3: hashset: start -- 231.26 % -- [Elapsed: 0.003749 sec]
4: hashset: end -- 368.74 % -- [Elapsed: 0.0059776 sec]
5: hashset: middle -- 385.50 % -- [Elapsed: 0.0062493 sec]
6: hashset: late -- 406.23 % -- [Elapsed: 0.0065854 sec]
7: hashset: early -- 421.34 % -- [Elapsed: 0.0068304 sec]
8: list: middle -- 18,619.12 % -- [Elapsed: 0.3018345 sec]
9: list: end -- 40,942.82 % -- [Elapsed: 0.663724 sec]
10: list: late -- 41,188.19 % -- [Elapsed: 0.6677017 sec]


---------- Testing few ints ------------
Sample items: (16 total)
7266092 60668895 159021363 216428460 28007724
...

Benchmarks:
1: hashset: early -- 100.00 % -- [Elapsed: 0.0016211 sec]
2: hashset: end -- 100.45 % -- [Elapsed: 0.0016284 sec]
3: list: early -- 101.83 % -- [Elapsed: 0.0016507 sec]
4: hashset: late -- 108.95 % -- [Elapsed: 0.0017662 sec]
5: hashset: middle -- 112.29 % -- [Elapsed: 0.0018204 sec]
6: hashset: start -- 120.33 % -- [Elapsed: 0.0019506 sec]
7: list: late -- 134.45 % -- [Elapsed: 0.0021795 sec]
8: list: start -- 136.43 % -- [Elapsed: 0.0022117 sec]
9: list: end -- 169.77 % -- [Elapsed: 0.0027522 sec]
10: list: middle -- 237.94 % -- [Elapsed: 0.0038573 sec]


---------- Testing many ints ------------
Sample items: (10357 total)
370826556 569127161 101235820 792075135 270823009
...

Benchmarks:
1: list: early -- 100.00 % -- [Elapsed: 0.0015132 sec]
2: hashset: end -- 101.79 % -- [Elapsed: 0.0015403 sec]
3: hashset: early -- 102.08 % -- [Elapsed: 0.0015446 sec]
4: hashset: middle -- 103.21 % -- [Elapsed: 0.0015618 sec]
5: hashset: late -- 104.26 % -- [Elapsed: 0.0015776 sec]
6: list: start -- 126.78 % -- [Elapsed: 0.0019184 sec]
7: hashset: start -- 130.91 % -- [Elapsed: 0.0019809 sec]
8: list: middle -- 16,497.89 % -- [Elapsed: 0.2496461 sec]
9: list: end -- 32,715.52 % -- [Elapsed: 0.4950512 sec]
10: list: late -- 33,698.87 % -- [Elapsed: 0.5099313 sec]


有趣.谢谢你的运行.可悲的是,我怀疑这些讨论引发了不必要的重构.希望对于大多数人来说,最好的情况是,在最糟糕的情况下,`List`仍然只需要0.17 _milliseconds_来执行单个查找,并且在查找频率达到荒谬级别之前不太可能需要替换`HashSet`.到那时,List的使用通常是最少的问题.

6> Walden Lever..:

盈亏平衡将取决于计算哈希的成本.散列计算可以是微不足道的,或者不是...... :-)总有System.Collections.Specialized.HybridDictionary类可以帮助您不必担心盈亏平衡点.



7> Robert P..:

答案一如既往,是" 这取决于 ".我假设你在谈论C#的标签.

你最好的选择是确定

    一组数据

    使用要求

并编写一些测试用例.

它还取决于您对列表进行排序的方式(如果它完全排序),需要进行何种比较,"比较"操作对列表中的特定对象进行多长时间,甚至取决于您打算如何使用采集.

通常,最好的选择不是基于您正在使用的数据大小,而是基于您打算如何访问它.您是否拥有与特定字符串或其他数据相关联的每条数据?基于哈希的集合可能是最好的.您存储的数据的顺序是否重要,或者您是否需要同时访问所有数据?常规列表可能会更好.

额外:

当然,我的上述评论假设"性能"意味着数据访问.还有其他需要考虑的因素:当你说"表现"时,你在寻找什么?绩效个人价值是否会抬头?管理大型(10000,100000或更多)价值集?是用数据填充数据结构的性能吗?删除数据?访问各个数据位?取代价值观?迭代价值观?内存使用情况?数据复制速度快?例如,如果按字符串值访问数据,但主要性能要求是最小内存使用量,则可能存在冲突的设计问题.



8> Muis..:

您可以使用HybridDictionary自动检测断点,并接受空值,使其基本上与HashSet相同.

推荐阅读
赛亚兔备_393
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有