HashSet <T>与Dictionary <K,V>的搜索时间以查找是否存在项


103
HashSet<T> t = new HashSet<T>();
// add 10 million items


Dictionary<K, V> t = new Dictionary<K, V>();
// add 10 million items.

谁的.Contains方法会更快返回?

为了明确起见,我的要求是我有1000万个对象(实际上是字符串),我需要检查它们是否存在于数据结构中。我永远不会重复。


1
第1步:查看两者是否做相同的事情(在这种情况下,这两个集合是出于不同的目的)。第2步:参考文档,看看您对它们的渐近复杂度是否满意。第3步:如果您觉得自己需要担心的多,请自己衡量一下,然后提出将基准与基准一同发布的问题。在您的情况下,第一步的问题变得毫无意义。
2014年

Answers:


153

HashSet vs List与Dictionary性能测试,取自此处

添加1000000个对象(不检查重复项)

包含检查对象10000的一半的集合

删除10000个集合的一半对象


9
很棒的分析!看起来.Contains for Dictionary是如此之快,以至于在OP的情况下使用HashSet根本没有任何好处。
以太龙

2
是的,我和OP有同样的问题。由于其他原因,我已经在使用字典,并想知道是否可以从更改为Hashset而不是使用ContainsKey中受益。看起来答案是否定的,因为两者都太快了。
FistOfFury 2012年

4
与前面的注释似乎相反,是的,应该切换到HashSet,因为它可以为您提供所需的内容:存储一组值(而不是维护某种映射)。该答案表明与Dictionary相比不会对性能产生负面影响。
Francois Beaussier '17

这个答案并不能告诉您HashSet和Dictionary的性能如何比较……它告诉您的是它们都比List更快。明显!HashSet可能快3倍,您不会知道,因为相关测试已崩溃为“ 与List相比,它们是瞬时的…… ”。
布隆达尔

71

我想你的意思Dictionary<TKey, TValue>是第二种情况?HashTable是一个非泛型类。

您应该根据实际需求为工作选择合适的收藏夹。您是否真的将每个键映射到一个值?如果是这样,请使用Dictionary<,>。如果您关心它,请使用HashSet<>

我希望HashSet<T>.ContainsDictionary<TKey, TValue>.ContainsKey(假设您明智地使用字典,这是可比的操作)基本上会执行相同的操作-从根本上讲,它们使用的是相同的算法。我在条目猜测Dictionary<,>是大你最终吹缓存更大的可能性Dictionary<,>HashSet<>,但是我希望有选择错误的数据类型的只是在你在做什么方面的痛苦相比,那是微不足道试图实现。


是的,我的意思是Dictionary <TKey,TValue>。我只关心搜索在数据结构项目的存在,那就是所有
哈利文斯顿2010年

3
@halivingston在这种情况下,请使用HashSet。很明显,这就是您所需要的。
乔恩·斯基特

2
好,谢谢。我实际上现在有一个HashSet <TKey>,并且在内存中也有Dictionary <Tkey,TValue>的副本。我首先。包含在HashSet上,然后检索Dictionary <TKey,TValue>中的值。我现在有无限的内存,但是很快我就会担心自己的内存会受到限制,我们的团队会要求我删除内存中的重复内容,这时我将被迫使用Dictionary <TKey,TValue>。
哈利文斯顿2010年

4
您知道Dictionary拥有一个ContainsKey函数吗?为什么要复制数据?
布林迪

8
如果字典中已经有数据,那么您的第一个注释显然不正确-您还需要将键与值相关联。也许不是针对这段特定的代码,但这无关紧要。如果Dictionary由于其他原因已经有了,则应该使用它。
乔恩·斯基特

7

来自Dictionary <TKey,TValue>的MSDN文档

“通过使用其键检索值非常快,接近O(1),因为Dictionary类是作为哈希表实现的。

带有注释:

“检索速度取决于为TKey指定的类型的哈希算法的质量”

我知道您的问题/帖子很旧-但是在寻找类似问题的答案时,我偶然发现了这个问题。

希望这可以帮助。向下滚动到“ 备注”部分以获取更多详细信息。 https://msdn.microsoft.com/zh-CN/library/xfhwa508(v=vs.110).aspx


4

这些是不同的数据结构。也没有的通用版本HashTable

HashSet包含类型T HashTable(或Dictionary)包含键值对的值。因此,您应该选择需要存储哪些数据的集合。


0

接受的对此问题的答案不能有效回答该问题!碰巧给出了正确的答案,但是他们提供的证据并未显示该答案。

该答案显示的是在DictionaryHashSet要快得多List。这是真的,但并不有趣,也不令人惊讶,也不能证明它们具有相同的含义速度。

我已经运行了下面的代码来比较查找时间,我的结论是它们实际上是相同的速度。(或者至少,如果存在任何差异,则该差异完全在该速度的标准偏差之内)

具体来说,对于我来说,在此测试中,两亿次查找都花费了10到11.5秒。

测试代码:

private const int TestReps = 100_000_000;
[Test]
public void CompareHashSetContainsVersusDictionaryContainsKey()
{
    for (int j = 0; j < 10; j++)
    {
        var rand = new Random();
        var dict = new Dictionary<int, int>();
        var hash = new HashSet<int>();

        for (int i = 0; i < TestReps; i++)
        {
            var key = rand.Next();
            var value = rand.Next();
            hash.Add(key);
            dict.TryAdd(key, value);
        }

        var testPoints = Enumerable.Repeat(1, TestReps).Select(_ => rand.Next()).ToArray();
        var timer = new Stopwatch();
        var total = 0;
        
        timer.Restart();
            for (int i = 0; i < TestReps; i++)
            {
                var newKey = testPoints[i];
                if (hash.Contains(newKey))
                {
                    total++;
                }
            }
        Console.WriteLine(timer.Elapsed);
        
        var target = total;
        Assert.That(total == target);
        

        timer.Restart();
            for (int i = 0; i < TestReps; i++)
            {
                var newKey = testPoints[i];
                if (dict.ContainsKey(newKey))
                {
                    total++;
                }
            }
        Console.WriteLine(timer.Elapsed);

        Assert.That(total == target * 2);
        Console.WriteLine("Set");
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.