Efficienza dei dizionari C #


14

I dizionari C # sono un modo semplice per scoprire se esiste qualcosa ecc. Ecc. Ho una domanda però su come funzionano. Diciamo invece di un dizionario che uso un ArrayList. Invece di usare ContainsKey(o un metodo equivalente in un'altra lingua) faccio un ciclo attraverso ArrayList per verificare se esiste qualcosa lì (o eseguendo una ricerca binaria se i dati sono ordinati o qualcosa di simile). Qual è la differenza in termini di efficienza? Il ContainsKeymetodo utilizza un modo più efficiente invece di scorrere ciclicamente i tasti e verificare se esiste ciò che sto cercando?

Se diciamo che avevo creato una specifica funzione di hash che corrisponde al tipo di dati che sto avendo ed è specificamente progettato per quel set di dati, allora sì, quella funzione di hash è davvero più veloce del loop attraverso i dati. Ma i dizionari sono generali. Il metodo ContainsKey non è specifico per i dati che ottiene, è un metodo di ricerca generale.

Fondamentalmente quello che sto chiedendo è. I dizionari sono utili ai programmatori. Includono metodi che aiutano con molte cose e combinano stringhe con numeri interi, (chiavi e valori) e molti altri. Ma per quanto riguarda l'efficienza, cosa offrono? Qual è la differenza nell'avere un dictionaryvs un ArrayListdistructs(string,int)


Stai davvero confrontando le mele con le arance qui. Penso che la parola chiave che stai cercando sia Data Structures Questo link wiki potrebbe essere di maggiore aiuto per te
Em

Risposte:


22

Devi scavare un po 'per vedere come il dizionario è implementato in C # - Non è così ovvio come HashMap (una tabella hash) o TreeMap (un albero ordinato) (o ConcurrentSkipListMap - un elenco salta ).

Se scavi nella sezione "Note":

La classe generica Dizionario fornisce una mappatura da un set di chiavi a un set di valori. Ogni aggiunta al dizionario è costituita da un valore e dalla chiave associata. Recuperare un valore usando la sua chiave è molto veloce, vicino a O (1), perché la classe Dictionary è implementata come una tabella hash.

E lì ce l'abbiamo. È una tabella hash . Nota che ho collegato l'articolo di Wikipedia lì - è una lettura abbastanza buona. Potresti voler leggere la sezione sulla risoluzione delle collisioni. È possibile ottenere un set di dati patologici in cui la ricerca si trasforma in O (N) (ad esempio tutto ciò che si inserisce rientra nello stesso valore di hash o indice nella tabella di hash per qualche motivo e si rimane con il sondaggio lineare ).

Mentre il dizionario è una soluzione per scopi generici, non dovresti passare da tipi concreti (come il dizionario), ma dovresti passare dalle interfacce. In questo caso, tale interfaccia è IDictionary( docs ). Per questo, sei perfettamente in grado di scrivere la tua implementazione del dizionario che fa le cose in modo ottimale per i dati che hai.

Quanto all'efficienza di varie ricerche / contiene?

  • Camminare su un elenco non ordinato: O (N)
  • Ricerca binaria di un array ordinato: O (registro N)
  • Albero ordinato: O (registro N)
  • Tabella hash: O (1)

Per la maggior parte delle persone, la tabella hash è ciò che vogliono.

Potresti scoprire che SortedDictionary è quello che vuoi invece:

La SortedDictionary<TKey, TValue>classe generica è un albero di ricerca binario con recupero O (log n), dove n è il numero di elementi nel dizionario. A questo proposito, è simile alla SortedList<TKey, TValue>classe generica. Le due classi hanno modelli di oggetti simili ed entrambe hanno il recupero O (log n).

Anche se, ancora una volta, se la struttura dei dati non è quella che funziona idealmente con i tuoi dati, ti vengono forniti gli strumenti (le interfacce) per essere in grado di scrivere quello che funziona meglio per i tuoi dati.

Il dizionario stesso è un tipo di dati astratto . Mi dai un dizionario e so cosa posso farci e tutti gli strumenti lì per me da usare per la natura di essere un dizionario. Se mi fornissi una ArrayList, mi ritroverei a scrivere il mio codice per cercare, inserire o eliminare elementi dall'elenco. Questo mi fa perdere tempo e significa anche che c'è più probabilità che si verifichi un bug mentre copio il codice ancora e ancora da un punto all'altro.


5
O (1) non è necessariamente "veloce". Il ciclo attraverso un elenco potrebbe essere ancora più veloce di una tabella hash per le dimensioni della raccolta con cui l'applicazione ha a che fare.
whatsisname

5
@whatsisname in nessun momento pretendo che O (1) sia veloce. Ha certamente il potenziale per essere il più veloce. L'iterazione su una delle chiavi di una tabella hash è più lenta di quella di una ArrayList (a meno che non si stia utilizzando qualcosa come la LinkedHashMap fornita da Java). È importante conoscere i tuoi dati e come si comportano e scegliere la raccolta appropriata per loro - e se non esiste, scriverli. Supponendo, ovviamente, che un tale sforzo valga davvero la pena del tempo (profilo prima!).

La tua citazione dice "Recuperare un valore usando la sua chiave è molto veloce, vicino a O (1), perché la classe Dictionary è implementata come una tabella hash.", Così l'OP potrebbe confondere i due concetti. In altre parole, volevo chiarire che la grande O non racconta l'intera storia riguardante la "velocità".
whatsisname

3
@whatsisname che è diretto da Microsoft. Usare una chiave per cercare un valore, a meno che tu non abbia una hashtable patologica (che risolve le collisioni di hash con qualche altro meccanismo) sarà più veloce che cercarla in un albero o in un elenco ordinato (o in un elenco non ordinato). Java, ad esempio, utilizza il probing lineare (passaggio 1) per la risoluzione delle collisioni, che può essere più lenta nei casi in cui la tabella è troppo piena o troppi hash si scontrano. Per il caso generale, tuttavia, è abbastanza buono.

Come esempio pertinente, di recente ho ottimizzato del codice in c ++ che originariamente utilizzava una tabella hash per set di dati di circa 20 voci e impiegava circa 400 ms per il completamento. Il passaggio a un albero binario lo ha portato a 200 ms, perché l'albero è più semplice da accedere. Ma sono stato in grado di tagliarlo ulteriormente utilizzando una serie di coppie valore-nome e una funzione di ricerca euristica che indovinava da dove iniziare a guardare in base ai modelli di accesso passati. Quindi è tutta una questione di quanti dati ci sono e che tipo di pattern ci sono negli accessi (ad es. Località).
Jules,
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.