HashSet <T> ile Sözlük <K, V> arasında bir öğe olup olmadığını bulmak için arama süresi


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


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

Kimin .Contains yöntemi daha hızlı dönecek?

Sadece açıklığa kavuşturmak için, benim ihtiyacım, veri yapısında var olup olmadıklarını kontrol etmem gereken 10 milyon nesneye (aslında, dizgiler) sahip olmaktır. ASLA yinelemeyeceğim.


1
Adım 1: Her ikisinin de aynı şeyi yapıp yapmadığını görün (bu durumda, iki koleksiyon farklı amaçlar içindir) Adım 2: Belgelere bakın ve asimptotik karmaşıklıkları hakkında iyi hissedip hissetmediğinizi görün. Adım 3: Daha fazla endişelenmeniz gerektiğini düşünüyorsanız, kendinizi ölçün ve ardından kıyaslama ölçütü ile birlikte soruyu sorun. Sizin durumunuzda, soru ilk adımda anlamsız hale geliyor.
nawfal

Yanıtlar:


153

HashSet vs List vs Dictionary performans testi buradan alınmıştır .

1000000 nesne ekleyin (kopyaları kontrol etmeden)

10000 kişilik bir koleksiyonun nesnelerinin yarısını kontrol içerir

10000 kişilik bir koleksiyondaki nesnelerin yarısını kaldırın


9
Harika analiz! Görünüşe göre .Contains for Dictionary o kadar hızlı ki, OP'nin durumunda HashSet kullanmanın hiçbir faydası yok.
EtherDragon

2
evet, OP ile aynı soruyu sormuştum. Zaten başka nedenlerle kullandığım bir sözlüğüm var ve ContainsKey kullanmak yerine Hashset'e geçmekten fayda sağlayıp sağlamayacağımı bilmek istedim. Görünüşe göre cevap hayır, çünkü ikisi de çok hızlı.
FistOfFury

4
Önceki yorumların ima ettiğinin aksine, evet, HashSet'e geçmelisiniz çünkü size istediğinizi verir: bir dizi değer depolamak (bir tür eşlemeyi sürdürmenin aksine). Bu cevap, sözlüğe kıyasla performans üzerinde olumsuz bir etkinin olmayacağını göstermektedir.
Francois Beaussier

Bu cevap size HashSet ve Sözlük performansının nasıl karşılaştırıldığını SÖYLEMEZ ... size tek söylediği, her ikisinin de bir Listeden daha hızlı olduğu ... peki ... evet! Açıkçası! HashSet 3 kat daha hızlı olabilir ve siz bilemezsiniz çünkü ilgili test hem "Anlık ... bir Listeye kıyasla " çöktü .
Brondahl

71

Dictionary<TKey, TValue>İkinci durumda demek istediğinizi varsayıyorum ? HashTablegenel olmayan bir sınıftır.

Gerçek gereksinimlerinize göre iş için doğru koleksiyonu seçmelisiniz. Aslında her anahtarı bir değerle eşlemek istiyor musunuz ? Eğer öyleyse, kullanın Dictionary<,>. Eğer varsa sadece bir dizi, kullanılması gibi bu konuda bakım HashSet<>.

Sanırım HashSet<T>.Containsve Dictionary<TKey, TValue>.ContainsKey(sözlüğünüzü mantıklı bir şekilde kullandığınızı varsayarak karşılaştırılabilir işlemler) temelde aynısını yapmasını bekliyorum - temelde aynı algoritmayı kullanıyorlar. Ben girdilerle tahmin Dictionary<,>sizinle önbelleği üfleme çıkma ihtimalini ile bitirmek daha büyük olma Dictionary<,>ile daha HashSet<>, ama bu sadece ne konum açısından yanlış veri türünü seçme ağrı ile karşılaştırıldığında önemsiz olmasını beklediğimizi başarmaya çalışıyor.


Evet, Sözlük <TKey, TValue> demek istedim. Sadece bir veri yapısında öğenin varlığını aramakla ilgileniyorum, hepsi bu .
halivingston

3
@halivingston Bu durumda HashSet kullanın. O O kadar belli yapar ise tek ihtiyacınız.
Jon Skeet

2
Tamam teşekkürler. Aslında şu anda bir HashSet <TKey> ve aynı zamanda bellekte Dictionary <Tkey, TValue> kopyasının bir kopyası var. Önce ben. HashSet'i içerir, sonra Dictionary <TKey, TValue> içindeki değeri alır. Şu anda sonsuz hafızam var, ancak yakında hafızamın kısıtlanacağından korkuyorum ve ekibimiz benden bu yinelenen şeyleri hafızadan kaldırmamı isteyecek, bu noktada Dictionary <TKey, TValue> kullanmak zorunda kalacağım.
halivingston

4
Sözlüğün bir ContainsKey işlevi olduğunu biliyorsunuz değil mi? Verileri neden kopyalıyorsunuz?
Blindy

8
Sözlüğe zaten verilere sahipseniz, ilk yorumunuz açıkça yanlıştır - anahtarları değerlerle de ilişkilendirmeniz gerekir. Belki bu özel kod parçası için değil , ama bu alakasız. Zaten Dictionarybaşka nedenlerden dolayı varsa, onu kullanmalısın.
Jon Skeet

7

Dictionary <TKey, TValue> için MSDN belgelerinden

"Bir değeri anahtarını kullanarak almak çok hızlıdır, O (1) değerine yakındır , çünkü Dictionary sınıfı bir karma tablo olarak uygulanır . "

Bir notla:

"Geri alma hızı, TKey için belirtilen türdeki karma algoritmanın kalitesine bağlıdır"

Sorunuzun / gönderinizin eski olduğunu biliyorum - ancak benzer bir soruya yanıt ararken bununla karşılaştım.

Bu yardımcı olur umarım. Daha fazla ayrıntı için Açıklamalar bölümüne kaydırın . https://msdn.microsoft.com/en-us/library/xfhwa508(v=vs.110).aspx


4

Bunlar farklı veri yapılarıdır. Ayrıca genel bir sürümü yokturHashTable .

HashSetanahtar / değer çiftlerini içeren HashTable(veya Dictionary) T türü değerleri içerir. Bu nedenle, hangi verilerin depolanması gerektiğine dair koleksiyon seçmelisiniz.


0

Bu soruya verilen kabul edilen cevap, soruyu geçerli bir şekilde YANITLAMAZ! Doğru cevabı verir, ancak bu cevap, sundukları kanıtlarla gösterilmez.

Bu cevabın gösterdiği şey, a'da Anahtar aramalarının Dictionaryveya HashSeta'da aramaktan çok daha hızlı olduğudur List. Bu doğru, ama ilginç değil, şaşırtıcı değil, aynı hıza sahip olduklarının kanıtı .

Arama sürelerini karşılaştırmak için aşağıdaki kodu çalıştırdım ve sonucum bunların aslında aynı hızda olduklarıdır. (Ya da en azından herhangi bir fark varsa, o zaman fark o hızın Standart Sapması dahilindedir)

Özellikle, bu testte benim için 100.000.000 arama her ikisi için de 10 ila 11,5 saniye sürüyordu.

Test Kodu:

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");
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.