2009-06-17 3 views
119

Ho 60k elementi che devono essere controllati rispetto a un elenco di ricerca 20k. Esiste un oggetto di raccolta (ad esempio List, HashTable) che fornisce un metodo eccezionalmente veloce Contains()? O dovrò scrivere il mio? In altre parole, è il metodo predefinito Contains() solo scansionare ogni elemento o utilizza un algoritmo di ricerca migliore.Quale raccolta .NET fornisce la ricerca più veloce

foreach (Record item in LargeCollection) 
{ 
    if (LookupCollection.Contains(item.Key)) 
    { 
     // Do something 
    } 
} 

Nota. L'elenco di ricerca è già ordinato.

+0

Contiene per Elenco non funziona per l'elenco di oggetti perché sta confrontando i riferimenti. – Fiur

+2

Dati ordinati? Ricerca binaria - vedi @ Risposta di Mark. –

+0

HashtTable batte qualsiasi cosa fino a 2m elementi nella mia esperienza –

risposta

111

Nel caso più generale, considerare System.Collections.Generic.HashSet come la struttura di dati del cavallo di lavoro "Contiene" predefinita, poiché è necessario un tempo costante per valutare Contains.

La risposta effettiva a "Qual è la raccolta di ricerche più veloce" dipende dalle dimensioni dei dati, dall'ordinanza, dal costo di hashing e dalla frequenza di ricerca.

+23

Nota: non dimenticare di sovrascrivere la funzione hashcode. Per prestazioni aggiuntive, preesistere il codice hash nel costruttore. – Brian

+0

@Brian: buon punto. Stavo dando per scontato (inutilmente) Record.Key era un tipo costruito di qualche tipo. – Jimmy

+0

Record.Key è solo un lungo –

58

Se non avete bisogno di ordine, provare HashSet<Record> (di nuovo da 3,5 NET)

Se non, utilizza un List<Record> e chiamare BinarySearch.

+6

Oppure, in .NET> = 4, utilizzare [SortedSet] (http://msdn.microsoft.com/en-us/library/dd412070.aspx) – StriplingWarrior

19

Avete considerato List.BinarySearch(item)?

Hai detto che la tua vasta raccolta è già stata ordinata, quindi questa sembra l'occasione perfetta? Un hash sarebbe sicuramente il più veloce, ma questo comporta i suoi problemi e richiede molto più overhead per l'archiviazione.

+1

Hai ragione, un hash può portare alcuni problemi indesiderati quando si usano oggetti mutabili come chiave. – jmservera

2

Se non si è preoccupati di cigolare ogni singolo bit di prestazioni, il suggerimento di utilizzare una ricerca hash o binaria è solido. I tuoi set di dati non sono abbastanza grandi da rendere questo problema il 99% delle volte.

Ma se questo solo una delle migliaia di volte lo farai e le prestazioni sono critiche (e dimostrate inaccettabili usando HashSet/binary search), potresti sicuramente scrivere il tuo algoritmo che ha fatto il giro degli elenchi ordinati facendo confronti come sei andato Ogni lista dovrebbe essere percorsa al massimo una volta e nei casi patologici non sarebbe male (una volta percorsa questa rotta probabilmente si scoprirà che il confronto, supponendo che sia una stringa o altro valore non integrale, sarebbe la vera spesa e che ottimizzando quello sarebbe il prossimo passo).

3

Se è possibile ordinare i tuoi articoli, allora c'è un modo molto più veloce per fare questo, quindi fare ricerche chiave in un hashtable o b-tree. Anche se gli oggetti non sono ordinabili, non puoi comunque metterli in un b-tree.

In ogni caso, se ordinate ordinatamente entrambi gli elenchi, si tratta solo di percorrere l'elenco di ricerca in ordine.

Walk lookup list 
    While items in check list <= lookup list item 
    if check list item = lookup list item do something 
    Move to next lookup list item 
+0

Sì, è così vero. Se hai due elenchi ordinati devi solo attraversarli ogni volta. – denver

2

Se stai usando Net 3.5, è possibile rendere il codice più pulito utilizzando:

foreach (Record item in LookupCollection.Intersect(LargeCollection)) 
{ 
    //dostuff 
} 

io non ho Net 3.5 qui e quindi questo è non testati. Si basa su un metodo di estensione. Non è che LookupCollection.Intersect(LargeCollection) probabilmente non è lo stesso di LargeCollection.Intersect(LookupCollection) ... quest'ultimo è probabilmente molto più lento.

Questo presuppone LookupCollection è un HashSet

4

tenere entrambe le liste X e Y in modo ordinato.

Se x = y, eseguire l'operazione, se x < y, anticipo x, se y < x, avanzare y fino a quando una lista è vuota.

Il tempo di esecuzione di questa intersezione è proporzionale min (dimensione (x), dimensione (y))

Non eseguire un ciclo .Contains(), questo è proporzionale x * y che è molto peggio.

+0

+1 per l'algoritmo più efficiente. Anche se gli elenchi sono attualmente non ordinati, sarebbe più efficiente innanzitutto ordinarli e quindi eseguire questo algoritmo. –

+0

Tuttavia, il runtime non sarebbe proporzionale a max (dimensione (x), dimensione (y)) nello scenario peggiore? Esempio: int [] x = {99,100}; int [] y = {0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; –

+0

No, perché una volta completato il set più piccolo, è possibile aggiungere gli elementi rimanenti dal set più grande perché sono già ordinati. Penso che questo processo sia simile a Merge Sort. –

8

Si dovrebbe leggere this blog che la velocità ha verificato diversi tipi diversi di raccolte e metodi per ciascuna utilizzando sia tecniche singole che multi-thread.

In base ai risultati, una ricerca binaria su una lista e una lista ordinata erano i migliori artisti che eseguivano costantemente il collo in collo quando guardavano qualcosa come un "valore".

Quando si utilizza una raccolta che consente "chiavi", il dizionario, ConcurrentDictionary, Hashset e HashTables hanno ottenuto il miglior risultato complessivo.