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方が早く戻るの?

明確にするために、私の要件は、データ構造内に存在するかどうかを確認する必要があるオブジェクトが1,000万個(実際には文字列)あることです。私は決して反復しません。


1
ステップ1:両方が同じことを行うかどうかを確認します(この場合、2つのコレクションは異なる目的のためです)ステップ2:ドキュメントを参照し、漸近的な複雑さについて気分が良いかどうかを確認します。ステップ3:もっと心配する必要があると感じた場合は、自分で測定してから、ベンチマークと一緒に質問を投稿してください。あなたの場合、質問は最初のステップで無意味になります。
nawfal

回答:


153

HashSet vs List vs Dictionaryパフォーマンステスト。ここから取得。

1000000個のオブジェクトを追加します(重複をチェックせずに)

10000のコレクションのオブジェクトの半分のチェックが含まれています

10000のコレクションのオブジェクトの半分を削除します


9
すばらしい分析!OPの場合、.Contains for Dictionaryは非常に高速であるため、HashSetを使用するメリットはまったくありません。
EtherDragon 2012

2
ええ、私はOPと同じ質問をしました。他の理由で使用している辞書がすでにあり、ContainsKeyを使用する代わりにHashsetに変更することのメリットがあるかどうかを知りたいと思っていました。どちらもとても速いので、答えはノーのようです。
FistOfFury

4
前のコメントが示唆しているように見えるのとは対照的に、はい、必要なものを提供するのでHashSetに切り替える必要があります:値のセットの格納(ある種のマッピングを維持するのではなく)。この回答は、ディクショナリと比較してパフォーマンスに悪影響がないことを示しています。
Francois Beaussier 2017

この回答は、HashSetとディクショナリのパフォーマンスがどのように比較されるかを示していません...両方がリストよりも速いことを示しているだけです。明らかに!HashSetのは3倍速いことができ、関連するテストは、「彼らは、瞬間だ...と、両方のダウン崩壊したので、あなたは知らないでしょうリストと比較します」。
ブロンダール

71

私はあなたDictionary<TKey, TValue>が2番目のケースで意味していると思いますか?HashTable非ジェネリッククラスです。

実際の要件に基づいて、ジョブに適したコレクションを選択する必要があります。あなたが実際に行う必要値に各キーをマップするために?その場合は、を使用してくださいDictionary<,>。セットとしてのみ気にする場合は、を使用してくださいHashSet<>

私は期待HashSet<T>.ContainsしてDictionary<TKey, TValue>.ContainsKey、彼らは基本的に、同じアルゴリズムを使用している-基本的に同じことを実行するために(あなたは賢明に自分の辞書を使用していると仮定すると、同等の操作です)。私は内のエントリと推測Dictionary<,>あなたがキャッシュを吹いての大きな尤度で終わる大きいことDictionary<,>よりもHashSet<>、私は単にあなたがしているものに関してそれは間違ったデータ型を選択するの痛みに比べて微々たるものであることを期待したいです達成しようとしています。


はい、私はDictionary <TKey、TValue>を意味しました。私は、データ構造内のアイテムの存在を検索することだけに関心があります。それだけです。
halivingston 2010

3
@halivingstonその場合はHashSetを使用してください。それは明らかにそれがあることになりあるあなたが必要とするすべて。
Jon Skeet、2010

2
わかりました。現在、実際にはHashSet <TKey>があり、メモリ内にもDictionary <Tkey、TValue>の複製コピーがあります。まずHashSetに.Containsを指定し、次にDictionary <TKey、TValue>の値を取得します。私は現在無限のメモリを持っていますが、すぐにメモリが制限されるのではないかと恐れて、メモリ内のこの重複したものを削除するようにチームから求められます。
halivingston 2010

4
辞書にContainsKey関数が含まれていることもご存じですか。なぜデータを複製するのですか?
ブリンディ2010

8
辞書にすでにデータがある場合、最初のコメントは明らかに正しくありません。キーを値に関連付ける必要もあります。たぶん、この特定のコードではないかもしれませんが、それは無関係です。Dictionary他の理由ですでにを取得している場合は、それを使用する必要があります。
Jon Skeet、2010

7

辞書<TKey、TValue>のMSDNドキュメントから

「Dictionaryクラスはハッシュテーブルとして実装されているため、キーを使用した値の取得は非常に高速で、O(1)に近くなります。

メモ付き:

「検索の速度は、TKeyに指定されたタイプのハッシュアルゴリズムの品質に依存します」

私はあなたの質問/投稿が古いことを知っています-しかし、同様の質問への回答を探している間に私はこれに偶然出会いました。

お役に立てれば。詳細については、[ 備考]セクションまでスクロールしてください。 https://msdn.microsoft.com/en-us/library/xfhwa508(v=vs.110).aspx


4

これらは異なるデータ構造です。また、の汎用バージョンはありませんHashTable

HashSetキーと値のペアを含むタイプTの値が含まれますHashTable(またはDictionary)。したがって、格納する必要のあるデータのコレクションを選択する必要があります。


0

この質問に対する承認された回答は、質問に有効に回答するものではありません。それはたまたま正しい答えを与えるが、その答えは彼らが提供した証拠によって示されていない。

その答えが示すのは、Dictionaryまたはでのキールックアップは、でのルックアップHashSetよりもはるかに速いということListです。これは事実ですが、興味深いものでも、驚くべきものでもありませんし、速度が同じであることの証明でもありません。

以下のコードを実行してルックアップ時間を比較しましたが、私の結論は、それらは実際には同じ速度であるということです。(または、少なくとも、何らかの違いがある場合、違いはその速度の標準偏差内に十分あります)

具体的には、このテストでは、1億回のルックアップの両方に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");
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.