SortedList <>, SortedDictionary <> e Dictionary <>


Risposte:


101
  1. Durante l'iterazione degli elementi in uno dei due, gli elementi verranno ordinati. Non così con Dictionary<T,V>.

  2. MSDN risolve la differenza tra SortedList<T,V>e SortedDictionary<T,V>:

La classe generica SortedDictionary (TKey, TValue) è un albero di ricerca binario con recupero O (log n), dove n è il numero di elementi nel dizionario. A questo proposito, è simile alla classe generica SortedList (TKey, TValue). Le due classi hanno modelli a oggetti simili ed entrambe hanno il recupero O (log n). Dove le due classi differiscono è nell'uso della memoria e nella velocità di inserimento e rimozione:

SortedList (TKey, TValue) utilizza meno memoria di SortedDictionary (TKey, TValue).

SortedDictionary (TKey, TValue) ha operazioni di inserimento e rimozione più veloci per i dati non ordinati: O (log n) anziché O (n) per SortedList (TKey, TValue).

Se l'elenco viene popolato tutto in una volta dai dati ordinati, SortedList (TKey, TValue) è più veloce di SortedDictionary (TKey, TValue).


21
Un'altra differenza pratica, quella in SortedListcui puoi recuperare per indice (in opposizione al recupero tramite chiave) e in SortedDictionaryche non puoi.
Andrew Savinykh

65

inserisci qui la descrizione dell'immagine

Menzionerei la differenza tra i dizionari.

L'immagine sopra mostra che Dictionary<K,V>è uguale o più veloce in ogni caso rispetto Sortedall'analogico, ma se è richiesto l'ordine degli elementi, ad esempio per stamparli, ne Sortedviene scelto uno.

Src: http://people.cs.aau.dk/~normark/oop-csharp/html/notes/collections-note-time-complexity-dictionaries.html


1
Ottima panoramica. Anche se non nella domanda originale, va notato che se si sceglie tra le Immutableversioni di questi dizionari, le Sortedversioni sono spesso in realtà più veloci di circa il 40-50% rispetto alle controparti non ordinate (ancora O(log(n)), ma notevolmente più veloci per operazione) . Tuttavia, i tempi possono variare a seconda di come è ordinato l'ingresso. Vedi stackoverflow.com/a/30638592/111575
Abel

21

Per riepilogare i risultati di un test delle prestazioni - SortedList vs SortedDictionary vs Dictionary vs Hashtable , i risultati dal migliore al peggiore per diversi scenari:

Utilizzo della memoria:

SortedList<T,T>
Hashtable
SortedDictionary<T,T>
Dictionary<T,T>

Inserimenti:

Dictionary<T,T>
Hashtable
SortedDictionary<T,T>
SortedList<T,T>

Operazioni di ricerca:

Hashtable
Dictionary<T,T>
SortedList<T,T>
SortedDictionary<T,T>

operazioni di ciclo foreach

SortedList<T,T>
Dictionary<T,T>
Hashtable
SortedDictionary<T,T>

1
Quando si esaminano i risultati di questi test, si può mettere in dubbio la ragion d'essere di SortedDictionary.
beawolf

1
Se hai Collectionbisogno di esserlo sorted, puoi dimenticartene Hashtablee Dictionary: se compili la tua raccolta in un colpo solo -> vai su SortedList, ma se prevedi ti servirà spesso .Adde .Removeelementi -> scegli SortedDictionary.
Ama

Forse c'è bisogno di chiarire cosa sortedsignifica: quando si fa a For Each MyItem in Collectioninvece di essere elaborati nell'ordine in cui sono stati originariamente modificati .Addgli articoli, a sorted Collectionli elaborerà in un ordine secondo criteri sui Keyvalori (definiti in an IComparer). Ad esempio, se le tue chiavi sono stringhe, la tua raccolta verrà elaborata per impostazione predefinita in base all'ordine alfabetico delle tue chiavi, ma puoi sempre definire una regola di ordinamento personalizzata.
Ama

9
  1. Quando si desidera che la raccolta venga ordinata per chiave quando si scorre su di essa. Se non hai bisogno che i tuoi dati vengano ordinati, stai meglio con solo un dizionario, avrà prestazioni migliori.

  2. SortedList e SortedDictionary fanno praticamente la stessa cosa, ma sono implementati in modo diverso, quindi hanno diversi punti di forza e di debolezza spiegati qui .


8

Vedo che le risposte proposte si concentrano sulle prestazioni. L'articolo fornito di seguito non fornisce nulla di nuovo riguardo alle prestazioni, ma spiega i meccanismi sottostanti. Si noti inoltre che non si concentra sui tre Collectiontipi menzionati nella domanda, ma si rivolge a tutti i tipi dello System.Collections.Genericspazio dei nomi.

http://geekswithblogs.net/BlackRabbitCoder/archive/2011/06/16/c.net-fundamentals-choosing-the-right-collection-class.aspx

Estratti:

Dizionario <>

Il Dictionary è probabilmente la classe contenitore associativa più utilizzata. Il Dictionary è la classe più veloce per ricerche / inserimenti / eliminazioni associative perché utilizza una tabella hash sotto le coperte . Poiché le chiavi sono sottoposte ad hashing, il tipo di chiave dovrebbe implementare correttamente GetHashCode () ed Equals () in modo appropriato oppure fornire un IEqualityComparer esterno al dizionario durante la costruzione. Il tempo di inserimento / cancellazione / ricerca degli elementi nel dizionario è tempo costante ammortizzato - O (1) - il che significa che non importa quanto sia grande il dizionario, il tempo necessario per trovare qualcosa rimane relativamente costante. Ciò è altamente desiderabile per le ricerche ad alta velocità. L'unico svantaggio è che il dizionario, per natura dell'utilizzo di una tabella hash, non è ordinato, quindinon è possibile attraversare facilmente gli elementi in un dizionario in ordine .

SortedDictionary <>

SortedDictionary è simile al Dictionary nell'uso ma molto diverso nell'implementazione. Il SortedDictionary utilizza un albero binario sotto le coperte per mantenere gli elementi in ordine dal tasto . Come conseguenza dell'ordinamento, il tipo utilizzato per la chiave deve implementare correttamente IComparable in modo che le chiavi possano essere ordinate correttamente. Il dizionario ordinato scambia un po 'di tempo di ricerca per la capacità di mantenere gli elementi in ordine, quindi i tempi di inserimento / cancellazione / ricerca in un dizionario ordinato sono logaritmici - O (log n). In generale, con il tempo logaritmico, puoi raddoppiare le dimensioni della collezione e devi solo eseguire un confronto extra per trovare l'oggetto. Usa SortedDictionary quando vuoi ricerche veloci ma vuoi anche essere in grado di mantenere la raccolta in ordine dalla chiave.

SortedList <>

SortedList è l'altra classe di contenitori associativi ordinati nei contenitori generici. Ancora una volta SortedList, come SortedDictionary, utilizza una chiave per ordinare le coppie chiave-valore . A differenza di SortedDictionary, tuttavia, gli elementi in un SortedList vengono archiviati come array di elementi ordinati. Ciò significa che gli inserimenti e le eliminazioni sono lineari - O (n) - perché l'eliminazione o l'aggiunta di un elemento può comportare lo spostamento di tutti gli elementi in alto o in basso nell'elenco. Il tempo di ricerca, tuttavia, è O (log n) perché SortedList può utilizzare una ricerca binaria per trovare qualsiasi elemento nell'elenco tramite la sua chiave. Allora perché mai dovresti volerlo fare? Bene, la risposta è che se hai intenzione di caricare il SortedList in anticipo, gli inserimenti saranno più lenti, ma poiché l'indicizzazione dell'array è più veloce dei seguenti collegamenti a oggetti, le ricerche sono leggermente più veloci di un SortedDictionary. Ancora una volta lo userei in situazioni in cui desideri ricerche veloci e vuoi mantenere la raccolta in ordine per chiave e dove gli inserimenti e le cancellazioni sono rari.


Riepilogo provvisorio delle procedure sottostanti

Il feedback è molto gradito perché sono sicuro di non aver fatto tutto bene.

  • Tutti gli array sono di dimensioni n.
  • Array non ordinato = .Add / .Remove è O (1), ma .Item (i) è O (n).
  • Matrice ordinata = .Add / .Remove è O (n), ma .Item (i) è O (log n).

Dizionario

Memoria

KeyArray(n) -> non-sorted array<pointer>
ItemArray(n) -> non-sorted array<pointer>
HashArray(n) -> sorted array<hashvalue>

Inserisci

  1. Aggiungi HashArray(n) = Key.GetHash# O (1)
  2. Aggiungi KeyArray(n) = PointerToKey# O (1)
  3. Aggiungi ItemArray(n) = PointerToItem# O (1)

Rimuovere

  1. For i = 0 to n, trova idove HashArray(i) = Key.GetHash # O (log n) (array ordinato)
  2. Rimuovi HashArray(i)# O (n) (array ordinato)
  3. Rimuovi KeyArray(i)# O (1)
  4. Rimuovi ItemArray(i)# O (1)

Ottieni oggetto

  1. For i = 0 to n, trova idove HashArray(i) = Key.GetHash# O (log n) (array ordinato)
  2. Ritorno ItemArray(i)

Loop Through

  1. For i = 0 to n, ritorno ItemArray(i)

SortedDictionary

Memoria

KeyArray(n) = non-sorted array<pointer>
ItemArray(n) = non-sorted array<pointer>
OrderArray(n) = sorted array<pointer>

Inserisci

  1. Aggiungi KeyArray(n) = PointerToKey# O (1)
  2. Aggiungi ItemArray(n) = PointerToItem# O (1)
  3. For i = 0 to n, trova idove KeyArray(i-1) < Key < KeyArray(i)(usando ICompare) # O (n)
  4. Aggiungi OrderArray(i) = n# O (n) (array ordinato)

Rimuovere

  1. For i = 0 to n, trova idove KeyArray(i).GetHash = Key.GetHash# O (n)
  2. Rimuovi KeyArray(SortArray(i))# O (n)
  3. Rimuovi ItemArray(SortArray(i))# O (n)
  4. Rimuovi OrderArray(i)# O (n) (array ordinato)

Ottieni oggetto

  1. For i = 0 to n, trova idove KeyArray(i).GetHash = Key.GetHash# O (n)
  2. Ritorno ItemArray(i)

Loop Through

  1. For i = 0 to n, ritorno ItemArray(OrderArray(i))

SortedList

Memoria

KeyArray(n) = sorted array<pointer>
ItemArray(n) = sorted array<pointer>

Inserisci

  1. For i = 0 to n, trova idove KeyArray(i-1) < Key < KeyArray(i)(usando ICompare) # O (log n)
  2. Aggiungi KeyArray(i) = PointerToKey# O (n)
  3. Aggiungi ItemArray(i) = PointerToItem# O (n)

Rimuovere

  1. For i = 0 to n, trova idove KeyArray(i).GetHash = Key.GetHash# O (log n)
  2. Rimuovi KeyArray(i)# O (n)
  3. Rimuovi ItemArray(i)# O (n)

Ottieni oggetto

  1. For i = 0 to n, trova idove KeyArray(i).GetHash = Key.GetHash# O (log n)
  2. Ritorno ItemArray(i)

Loop Through

  1. For i = 0 to n, ritorno ItemArray(i)

0

Cercando di assegnare un punteggio di prestazione a ciascun caso presentato da @Lev, ho utilizzato i seguenti valori:

  • O (1) = 3
  • O (log n) = 2
  • O (n) = 1
  • O (1) o O (n) = 2
  • O (log n) o O (n) = 1.5

I risultati sono (maggiore = migliore):

Dictionary:       12.0 
SortedDictionary:  9.0 
SortedList:        6.5

Naturalmente, ogni caso d'uso darà più peso a determinate operazioni.


1
Come regola generale, il peso di O (log n) sarebbe log (n) / log (2) (+1 ogni volta che n raddoppia) mentre il peso di O (n) sarebbe n. Quindi la tua ponderazione sarebbe corretta per taglie fino a 4. Qualunque cosa oltre vedrà il tuo rapporto 2: 1 aumentare rapidamente. Ad esempio, se n = 100 allora dovresti avere O (log n) = 15. Seguendo un pensiero simile, il tuo O (1) peserebbe 100. Conclusione: O (n) perde la battaglia abbastanza rapidamente. In caso contrario, significa che il tuo array è piccolo e quindi l'efficienza non è un problema.
Ama
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.