Quale raccolta .NET offre la ricerca più veloce


143

Ho 60k articoli che devono essere verificati in un elenco di ricerca 20k. Esiste un oggetto di raccolta (come List, HashTable) che fornisce un Contains()metodo eccezionalmente veloce ? O dovrò scrivere il mio? In altre parole, il Contains()metodo predefinito è semplicemente scansionare ogni elemento o utilizza un algoritmo di ricerca migliore.

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

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


Contiene per elenco non funziona per l'elenco di oggetti perché confronta i riferimenti.
Fiur,

2
Dati ordinati? Ricerca binaria - vedi la risposta di @ Mark.
Hamish Smith,

HashtTable batte qualsiasi cosa fino a 2m di oggetti nella mia esperienza
Chris S,

A parte questo, se i tuoi elementi sono in un ordine significativo e sono distribuiti in modo abbastanza uniforme, puoi fare una ricerca binaria molto più velocemente avendo le tue prime ipotesi all'interno di un intervallo stimato del tuo articolo. Questo può o meno avere un significato per la tua specifica applicazione.
Brian,

2
Non dimenticare System.Collections.Generic.SortedList (TKey, TValue) se vuoi semplificare queste cose ma evitare un hashset.
Brian,

Risposte:


141

Nel caso più generale, considera System.Collections.Generic.HashSetla struttura dei dati predefinita del cavallo di lavoro "Contiene", poiché richiede tempo costante per la valutazione Contains.

La risposta effettiva a "Qual è la raccolta più rapida per la ricerca" dipende dalle dimensioni specifiche dei dati, dall'ordine, dal costo dell'hash e dalla frequenza di ricerca.


36
Nota: non dimenticare di ignorare la funzione hashcode. Per prestazioni aggiuntive, crea il tuo codice hash nel costruttore.
Brian,

1
@Brian: buon punto. Stavo supponendo (senza fondamento) Record.Key era un tipo incorporato di qualche tipo.
Jimmy,

3
@Brian: invece di pregenerare preferisco conservare quello generato la prima volta, perché rallentare il costruttore con qualcosa che non sai se verrà utilizzato?
jmservera,

8
Cordiali saluti: Test delle prestazioni: ho creato un confronto tra List <T> e HashSet <T> per le stringhe. Ho scoperto che HashSet era circa 1000 volte più veloce di List.
Quango,

10
@Quango: 3 anni dopo, ma in realtà se non specifichi la dimensione del tuo set di dati questo confronto delle prestazioni non significa nulla: gli hash hanno la ricerca O (1), gli elenchi hanno la ricerca O (n), quindi il rapporto delle prestazioni è proporzionale a n.
Clément,

73

Se non hai bisogno di ordinare, prova HashSet<Record>(nuovo su .Net 3.5)

In tal caso, utilizzare a List<Record>e chiamare BinarySearch.


8
Oppure, in .NET> = 4, utilizzare SortedSet
StriplingWarrior il

2
O meglio, ImmutableSortedSetda System.ImmutableCollections
Alexei S

24

Hai preso in considerazione List.BinarySearch(item)?

Hai detto che la tua grande collezione è già ordinata, quindi questa sembra l'occasione perfetta? Un hash sarebbe sicuramente il più veloce, ma ciò comporta i propri 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,

10

Dovresti leggere questo blog che ha testato rapidamente diversi tipi di raccolte e metodi per ognuno usando tecniche sia single che multi-thread.

Secondo i risultati, BinarySearch on a List e SortedList sono stati i migliori artisti che correvano costantemente testa a testa quando cercavano qualcosa come un "valore".

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


4

Mantieni entrambi gli elenchi xey nell'ordine ordinato.

Se x = y, esegui la tua azione, se x <y, avanza di x, se y <x, avanza di y fino a quando uno dei due elenchi è vuoto.

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

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


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

Il runtime non sarebbe proporzionale a max (size (x), size (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};
Matt Boehm,

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.

3

Se è possibile ordinare i tuoi articoli, c'è un modo molto più veloce per farlo, quindi effettuare ricerche chiave in una tabella hash o b-tree. Tuttavia, se i tuoi oggetti non sono ordinabili, non puoi comunque metterli in un albero b.

Ad ogni modo, se ordinabile ordinare entrambi gli elenchi, allora è solo una questione di camminare nell'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

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

3

Se stai usando .Net 3.5, puoi creare un codice più pulito usando:

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

Non ho .Net 3.5 qui e quindi questo non è testato. 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.

Ciò presuppone che LookupCollection sia a HashSet


2

Se non sei preoccupato di scricchiolare fino all'ultimo bit di performance, il suggerimento di utilizzare un HashSet o una ricerca binaria è solido. I tuoi set di dati non sono abbastanza grandi da essere un problema il 99% delle volte.

Ma se questa è solo una delle migliaia di volte che lo farai e le prestazioni sono fondamentali (e si dimostrano inaccettabili usando HashSet / ricerca binaria), potresti certamente scrivere il tuo algoritmo che camminava sugli elenchi ordinati facendo confronti mentre procedevi. Ogni elenco verrebbe camminato al massimo una volta e nei casi patologici non sarebbe male (una volta intrapreso questa strada probabilmente scopriresti che il confronto, supponendo che sia una stringa o un altro valore non integrale, sarebbe la vera spesa e che l'ottimizzazione sarebbe il prossimo passo).

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.