HashSet <T> versus Dictionary <K, V> waktu pencarian wrt untuk menemukan apakah suatu item ada

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


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

.ContainsMetode siapa yang akan kembali lebih cepat?

Hanya untuk memperjelas, persyaratan saya adalah saya memiliki 10 juta objek (yah, string benar-benar) yang perlu saya periksa apakah mereka ada dalam struktur data. Saya TIDAK AKAN PERNAH mengulang.

halivingston
sumber
1
Langkah 1: Lihat apakah keduanya melakukan hal yang sama (dalam hal ini, dua koleksi untuk tujuan yang berbeda) Langkah 2: Lihat dokumentasi dan lihat apakah Anda merasa nyaman dengan kompleksitas asimtotiknya. Langkah 3: Jika Anda merasa perlu lebih khawatir, ukur diri Anda dan ajukan pertanyaan untuk memasang patokan bersamanya. Dalam kasus Anda, pertanyaan tersebut menjadi tidak berguna pada langkah pertama.
nawfal

Jawaban:

153

Tes kinerja HashSet vs List vs Dictionary, diambil dari sini .

Tambahkan 1000000 objek (tanpa memeriksa duplikat)

Berisi centang untuk setengah dari objek koleksi 10.000

Hapus setengah objek dari koleksi 10.000

punya
sumber
9
Analisis hebat! Sepertinya .Contains for Dictionary sangat cepat sehingga tidak ada manfaat menggunakan HashSet sama sekali, dalam kasus OP.
EtherDragon
2
ya, saya punya pertanyaan yang sama dengan OP. Saya sudah memiliki kamus yang saya gunakan untuk alasan lain, dan ingin tahu apakah saya mendapat manfaat dari mengubah ke Hashset daripada menggunakan ContainsKey. Sepertinya jawabannya tidak karena keduanya sangat cepat.
FistOfFury
4
Bertentangan dengan apa yang tampaknya disiratkan oleh komentar sebelumnya, ya, Anda harus beralih ke HashSet karena itu memberi Anda apa yang Anda inginkan: menyimpan satu set nilai (sebagai lawan mempertahankan semacam pemetaan). Jawaban ini menunjukkan bahwa tidak akan ada dampak negatif pada kinerja dibandingkan dengan Kamus.
Francois Beaussier
Jawaban ini TIDAK memberi tahu Anda bagaimana kinerja HashSet dan Kamus dibandingkan ... semua yang dikatakannya adalah bahwa keduanya lebih cepat daripada Daftar .. yah ... ya! Jelas! HashSet bisa 3 kali lebih cepat dan Anda tidak akan tahu karena tes yang relevan telah runtuh menjadi "mereka instan ... dibandingkan dengan Daftar ".
Brondahl
71

Saya berasumsi maksud Anda Dictionary<TKey, TValue>dalam kasus kedua? HashTableadalah kelas non-generik.

Anda harus memilih koleksi yang tepat untuk pekerjaan itu berdasarkan kebutuhan Anda yang sebenarnya. Apakah Anda benar - benar ingin memetakan setiap kunci ke sebuah nilai? Jika demikian, gunakan Dictionary<,>. Jika Anda hanya peduli sebagai satu set, gunakan HashSet<>.

Saya berharap HashSet<T>.Containsdan Dictionary<TKey, TValue>.ContainsKey(yang merupakan operasi yang sebanding, dengan asumsi Anda menggunakan kamus Anda dengan bijaksana) pada dasarnya melakukan hal yang sama - mereka menggunakan algoritma yang sama, pada dasarnya. Saya kira dengan entri Dictionary<,>menjadi lebih besar Anda berakhir dengan kemungkinan lebih besar untuk meniup cache dengan Dictionary<,>daripada dengan HashSet<>, tapi saya berharap itu tidak signifikan dibandingkan dengan rasa sakit memilih tipe data yang salah hanya dalam hal apa yang Anda mencoba untuk mencapai.

Jon Skeet
sumber
Ya, maksud saya Kamus <TKey, TValue>. Saya hanya memikirkan tentang mencari keberadaan item dalam struktur data, itu saja .
halivingston
3
@halivingston Dalam hal ini gunakan HashSet. Jelaslah bahwa hanya itu yang Anda butuhkan.
Jon Skeet
2
Ok terima kasih. Saya sebenarnya memiliki HashSet <TKey> sekarang, dan salinan duplikat Kamus <Tkey, TValue> juga di memori. Saya pertama. Berisi di HashSet, kemudian mengambil nilai dalam Kamus <TKey, TValue>. Saya memiliki memori tak terbatas saat ini, tetapi segera saya khawatir ingatan saya akan dibatasi dan tim kami akan meminta saya untuk menghapus barang duplikat ini di memori, di mana saya akan dipaksa untuk menggunakan Kamus <TKey, TValue>.
halivingston
4
Anda tahu Kamus memiliki fungsi ContainsKey juga kan? Mengapa Anda menduplikasi data?
Blindy
8
Jika Anda sudah memiliki datanya di kamus, maka komentar pertama Anda jelas salah - Anda perlu mengaitkan kunci dengan nilai juga. Mungkin tidak untuk ini sedikit kode tertentu, tapi itu tidak relevan. Jika Anda sudah punya Dictionarykarena alasan lain, Anda harus menggunakannya.
Jon Skeet
7

Dari dokumentasi MSDN untuk Kamus <TKey, TValue>

"Mengambil nilai dengan menggunakan kuncinya sangat cepat, mendekati O (1) , karena kelas Dictionary diimplementasikan sebagai tabel hash. "

Dengan catatan:

"Kecepatan pengambilan tergantung pada kualitas algoritme hashing dari jenis yang ditentukan untuk TKey"

Saya tahu pertanyaan / postingan Anda sudah lama - tetapi ketika mencari jawaban untuk pertanyaan serupa, saya menemukan ini.

Semoga ini membantu. Gulir ke bawah ke bagian Keterangan untuk lebih jelasnya. https://msdn.microsoft.com/en-us/library/xfhwa508(v=vs.110).aspx

ripvlan.dll
sumber
4

Ini adalah struktur data yang berbeda. Juga tidak ada versi generik HashTable.

HashSetberisi nilai tipe T yang HashTable(atau Dictionary) berisi pasangan nilai kunci. Jadi, Anda harus memilih koleksi tentang data apa yang perlu Anda simpan.

Andrew Bezzub
sumber
0

Jawaban yang diterima untuk pertanyaan ini TIDAK menjawab pertanyaan secara valid! Itu kebetulan memberikan jawaban yang benar, tetapi jawaban itu tidak ditunjukkan oleh bukti yang mereka berikan.

Apa yang ditunjukkan oleh jawaban itu adalah bahwa pencarian kunci di a Dictionaryatau HashSetjauh lebih cepat daripada mencari di a List. Yang benar, tapi tidak menarik, tidak mengherankan, atau bukti bahwa mereka memiliki kecepatan yang sama .

Saya telah menjalankan kode di bawah ini untuk membandingkan waktu pencarian, dan kesimpulan saya adalah bahwa sebenarnya kecepatannya sama. (Atau setidaknya, jika ada perbedaan, maka perbedaan tersebut berada dalam Standar Deviasi kecepatan tersebut)

Secara khusus, 100.000.000 pencarian membutuhkan waktu antara 10 dan 11,5 detik untuk keduanya, bagi saya, dalam pengujian ini.

Kode Tes:

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");
    }
}
Brondahl
sumber