HashSet <T> rispetto a Dictionary <K, V> rispetto al tempo di ricerca per trovare se un elemento esiste


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


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

Quale .Containsmetodo tornerà più rapidamente?

Giusto per chiarire, il mio requisito è che ho 10 milioni di oggetti (beh, davvero le stringhe) che devo controllare se esistono nella struttura dei dati. Non itererò MAI.


1
Passaggio 1: vedere se entrambi fanno la stessa cosa (in questo caso, le due raccolte hanno scopi diversi) Passaggio 2: fare riferimento alla documentazione e vedere se ti senti bene con la loro complessità asintotica. Passaggio 3: se ritieni di doverti preoccupare di più, misurati e poi fai la domanda pubblicando il benchmark insieme ad essa. Nel tuo caso la domanda diventa inutile nel primo passaggio.
nawfal

Risposte:


153

HashSet vs List vs Dictionary performance test, preso da qui .

Aggiungi 1000000 oggetti (senza controllare i duplicati)

Contiene il controllo per metà degli oggetti di una raccolta di 10000

Rimuovi metà degli oggetti di una collezione di 10.000


9
Ottima analisi! Sembra che .Contains for Dictionary sia così veloce che non c'è alcun vantaggio nell'usare HashSet, nel caso dell'OP.
EtherDragon,

2
sì, ho avuto la stessa domanda dell'OP. Ho già un dizionario che sto usando per altri motivi e volevo sapere se traggo vantaggio dal passaggio a un hashset invece di utilizzare ContainsKey. Sembra che la risposta sia no poiché entrambi sono così veloci.
FistOfFury

4
Contrariamente a quanto sembrano implicare i commenti precedenti, sì, dovresti passare a HashSet perché ti dà quello che vuoi: memorizzare un insieme di valori (invece di mantenere un qualche tipo di mappatura). Questa risposta indica che non ci sarà alcun impatto negativo sulle prestazioni rispetto a Dictionary.
Francois Beaussier

Questa risposta NON ti dice come si confrontano le prestazioni di HashSet e Dictionary ... tutto ciò che ti dice è che sono entrambi più veloci di un elenco .. beh ... sì! Ovviamente! HashSet potrebbe essere 3 volte più veloce e non lo sapresti perché il test pertinente è crollato entrambi in "sono istantanei ... rispetto a un elenco ".
Brondahl

71

Presumo che intendi Dictionary<TKey, TValue>nel secondo caso? HashTableè una classe non generica.

Dovresti scegliere la collezione giusta per il lavoro in base alle tue effettive esigenze. Si fa realmente desidera mappare ogni chiave ad un valore? Se è così, usa Dictionary<,>. Se ti interessa solo come set, usa HashSet<>.

Mi aspetto che HashSet<T>.Containse Dictionary<TKey, TValue>.ContainsKey(che sono le operazioni comparabili, supponendo che tu stia usando il tuo dizionario in modo sensato) fondamentalmente eseguano lo stesso - stanno usando lo stesso algoritmo, fondamentalmente. Immagino che con le voci Dictionary<,>più grandi si finisce con una maggiore probabilità di saltare la cache con Dictionary<,>che con HashSet<>, ma mi aspetto che sia insignificante rispetto al dolore di scegliere il tipo di dati sbagliato semplicemente in termini di ciò che stai cercando di raggiungere.


Sì, intendevo Dictionary <TKey, TValue>. Mi interessa solo cercare l'esistenza di un elemento in una struttura dati, tutto qui .
Halivingston

3
@halivingston In questo caso usa HashSet. È ovvio che questo è tutto ciò di cui hai bisogno.
Jon Skeet

2
Ok grazie. In questo momento ho un HashSet <TKey> e una copia duplicata di Dictionary <Tkey, TValue> anche in memoria. Per prima cosa .Contains su HashSet, quindi ritrovo il valore in Dictionary <TKey, TValue>. Ho una memoria infinita in questo momento, ma presto temo che la mia memoria sarà limitata e il nostro team mi chiederà di rimuovere questa roba duplicata in memoria, a quel punto sarò costretto a usare Dictionary <TKey, TValue>.
Halivingston

4
Sai che Dictionary ha anche una funzione ContainsKey, giusto? Perché stai duplicando i dati?
Blindy

8
Se hai già i dati nel dizionario, il tuo primo commento è chiaramente errato: devi associare anche le chiavi ai valori. Forse non per questo particolare pezzo di codice, ma è irrilevante. Se hai già un Dictionaryper altri motivi, dovresti usarlo.
Jon Skeet

7

Dalla documentazione MSDN per Dictionary <TKey, TValue>

"Recuperare un valore utilizzando la sua chiave è molto veloce, vicino a O (1) , perché la classe Dictionary è implementata come una tabella hash. "

Con una nota:

"La velocità di recupero dipende dalla qualità dell'algoritmo di hashing del tipo specificato per TKey"

So che la tua domanda / post è vecchia, ma mentre cercavo una risposta a una domanda simile mi sono imbattuto in questo.

Spero che questo ti aiuti. Scorri verso il basso fino alla sezione Osservazioni per maggiori dettagli. https://msdn.microsoft.com/en-us/library/xfhwa508(v=vs.110).aspx


4

Queste sono diverse strutture di dati. Inoltre non esiste una versione generica di HashTable.

HashSetcontiene valori di tipo T che HashTable(o Dictionary) contiene coppie chiave-valore. Quindi dovresti scegliere la raccolta su quali dati devi archiviare.


0

La risposta accettata a questa domanda NON risponde validamente alla domanda! Capita di dare la risposta corretta, ma quella risposta non è mostrata dalle prove che hanno fornito.

Ciò che mostra la risposta è che le ricerche di chiavi su a Dictionaryo HashSetsono molto più veloci di quelle in a List. Il che è vero, ma non interessante, né sorprendente, né prova che abbiano la stessa velocità.

Ho eseguito il codice seguente per confrontare i tempi di ricerca e la mia conclusione è che in realtà sono la stessa velocità. (O almeno, se c'è qualche differenza, allora la differenza è ben all'interno della deviazione standard di quella velocità)

In particolare, per me, in questo test, 100.000.000 di ricerche hanno richiesto tra 10 e 11,5 secondi per entrambi.

Codice di prova:

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");
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.