Strutture di dati .NET: ArrayList, List, HashTable, Dictionary, SortedList, SortedDictionary - Velocità, memoria e quando utilizzarle?


213

.NET ha molte strutture dati complesse. Sfortunatamente, alcuni di loro sono abbastanza simili, e non sono sempre sicuro quando usarne uno e quando usarne un altro. La maggior parte dei miei libri su C # e Visual Basic ne parlano in una certa misura, ma non entrano mai nei dettagli.

Qual è la differenza tra Array, ArrayList, List, Hashtable, Dictionary, SortedList e SortedDictionary?

Quali sono enumerabili (IList - può fare loop "foreach")? Quali usano coppie chiave / valore (IDict)?

Che dire dell'impronta di memoria? Velocità di inserimento? Velocità di recupero?

Ci sono altre strutture di dati degne di nota?

Sto ancora cercando ulteriori dettagli sull'utilizzo della memoria e sulla velocità (notazione Big-O).


12
Dovresti dividere questa domanda. Stai chiedendo venti cose diverse, metà delle quali una semplice ricerca su Google può rispondere. Sii più specifico; è difficile aiutare quando la tua domanda è così sparsa.

33
Ho pensato di romperlo, ma mi sono reso conto che qualcuno sarebbe stato in grado di consolidare tutte queste risposte in un unico posto. In effetti, se qualcuno può inventare un tavolo che profila tutto, potrebbe diventare una meravigliosa risorsa su questo sito.
Pretzel,

9
Questa domanda può essere trasformata in un wiki?
BozoJoe

1
Questo articolo MSDN copre molte di queste domande, inclusi alberi, grafici e set, Un esame approfondito delle strutture di dati
Ryan Fisher,

1
Ryan, gli articoli su quel link hanno 14 anni (12 al momento della posta). Nota a margine li ho letti per l'ultima settimana da solo. ma non includono anche la tecnologia più recente e hanno un disperato bisogno di aggiornamento. E più metriche ed esempi di rendimento.
htm11h

Risposte:


156

In cima alla mia testa:

  • Array* - rappresenta un array di memoria di vecchia scuola - un po 'come un alias per un type[]array normale . Può elencare. Non può crescere automaticamente. Assumerei una velocità di inserimento e riavvio molto elevata.

  • ArrayList- array in crescita automatica. Aggiunge più sovraccarico. Può enum., Probabilmente più lento di un array normale ma comunque piuttosto veloce. Questi sono usati molto in .NET

  • List- uno dei miei preferiti - può essere usato con generici, quindi puoi avere un array fortemente tipizzato, ad es List<string>. A parte questo, si comporta in modo molto simileArrayList

  • Hashtable- semplice vecchia hashtable. Caso peggiore da O (1) a O (n). Può enumerare il valore e le proprietà delle chiavi ed eseguire coppie chiave / val

  • Dictionary - Come sopra solo fortemente tipizzato tramite generici, come Dictionary<string, string>

  • SortedList- un elenco generico ordinato. Rallentata all'inserimento poiché deve capire dove collocare le cose. Può enum., Probabilmente lo stesso al momento del recupero poiché non deve ricorrere, ma la cancellazione sarà più lenta di un semplice vecchio elenco.

Tendo a usarlo Liste Dictionarysempre - una volta che inizi a usarli fortemente tipizzati con i generici, è davvero difficile tornare a quelli standard non generici.

Ci sono anche molte altre strutture di dati: c'è quella KeyValuePairche puoi usare per fare alcune cose interessanti, c'è anche una SortedDictionaryche può essere utile.


3
Hash Table è O (1), il caso peggiore (con collisioni) può essere O (n)
Justin Bozonier,

7
Ci sono molte altre strutture di dati che devi aggiungere qui. come LinkedList, Skip List, Stack, Queue, Heap, Trees, Graphs. Queste sono anche strutture di dati molto importanti.
DarthVader,

2
ConcurrentDictionary aggiunto in .Net 4.0 fornisce un dizionario generico con Thread Safety
Harindaka il

2
Inoltre BlockingCollection <T> fornisce un'implementazione produttore / consumatore thread-safe
Harindaka,

7
ArrayListusa metodi virtuali, ma List<T>non lo fa. ArrayListè stato in gran parte sostituito con List<T>per raccolte standard e Collection<T>come classe base per raccolte personalizzate. Hashtableè stato in gran parte sostituito da Dictionary<TKey, TValue>. Consiglierei di evitare ArrayListe Hashtableper il nuovo codice.
Sam Harwell,

29

Se possibile, usa i generici. Ciò comprende:

  • Elenco invece di ArrayList
  • Dizionario anziché HashTable

24

Innanzitutto, tutte le raccolte in .NET implementano IEnumerable.

In secondo luogo, molte raccolte sono duplicate perché i generici sono stati aggiunti nella versione 2.0 del framework.

Quindi, anche se le raccolte generiche probabilmente aggiungono funzionalità, per la maggior parte:

  • Elenco è un'implementazione generica di ArrayList.
  • Dizionario è un'implementazione generica di Hashtable

Le matrici sono una raccolta di dimensioni fisse che è possibile modificare il valore memorizzato in un determinato indice.

SortedDictionary è un IDictionary che viene ordinato in base alle chiavi. SortedList è un IDictionary che viene ordinato in base a un IComparer richiesto.

Quindi, le implementazioni IDictionary (quelle che supportano KeyValuePairs) sono: * Hashtable * Dictionary * SortedList * SortedDictionary

Un'altra raccolta che è stata aggiunta in .NET 3.5 è Hashset. È una raccolta che supporta operazioni set.

Inoltre, LinkedList è un'implementazione standard di elenchi collegati (l'elenco è un elenco di array per un recupero più rapido).


20

Ecco alcuni suggerimenti generali per te:

  • È possibile utilizzare foreachsui tipi che implementano IEnumerable. IListè essenzialmente una proprietà IEnumberablewith Counte Item(accesso agli oggetti usando un indice a base zero). IDictionaryd'altra parte significa che è possibile accedere agli elementi tramite qualsiasi indice hash.

  • Array, ArrayListE Listtutto implementare IList. Dictionary, SortedDictionarye Hashtableimplementare IDictionary.

  • Se si utilizza .NET 2.0 o versioni successive, si consiglia di utilizzare controparti generiche dei tipi citati.

  • Per la complessità temporale e spaziale di varie operazioni su questi tipi, è necessario consultare la loro documentazione.

  • Le strutture di dati .NET sono nello System.Collectionsspazio dei nomi. Esistono librerie di tipi come PowerCollections che offrono strutture di dati aggiuntive.

  • Per una comprensione approfondita delle strutture di dati, consultare risorse come CLRS .


1
da msdn , sembra che SortList implementa IDictionnary - non IList
Haim Bendanan

Fisso. grazie per il commento. Sembra che SortedList mantiene un elenco di chiavi / valori, quindi in pratica rappresenta i dati di un dizionario. Non ricordare come ha funzionato questa classe quando ho scritto la risposta per la prima volta ...
blackwing il

9

Strutture di dati .NET:

Altro sulla conversazione sul perché ArrayList e List sono effettivamente diversi

Array

Come afferma un utente, gli array sono la raccolta "vecchia scuola" (sì, gli array sono considerati una raccolta anche se non fanno parte di System.Collections). Ma che cos'è la "vecchia scuola" riguardo alle matrici rispetto ad altre raccolte, cioè quelle che hai elencato nel tuo titolo (qui, ArrayList and List (Of T))? Cominciamo con le basi guardando Array.

Per iniziare, gli array in Microsoft .NET sono "meccanismi che consentono di trattare diversi elementi [logicamente correlati] come un'unica raccolta" (vedere l'articolo collegato). Cosa significa? Gli array memorizzano i singoli membri (elementi) in sequenza, uno dopo l'altro in memoria con un indirizzo iniziale. Usando l'array, possiamo facilmente accedere agli elementi memorizzati in sequenza a partire da quell'indirizzo.

Oltre a ciò e contrariamente alla programmazione di 101 concezioni comuni, gli array possono davvero essere piuttosto complessi:

Le matrici possono essere monodimensionali, multidimensionali o jadded (vale la pena leggere le matrici frastagliate). Gli array stessi non sono dinamici: una volta inizializzato, un array di n dimensioni riserva spazio sufficiente per contenere n numero di oggetti. Il numero di elementi nell'array non può aumentare o diminuire. Dim _array As Int32() = New Int32(100)riserva spazio sufficiente sul blocco di memoria affinché l'array contenga 100 oggetti di tipo primitivo Int32 (in questo caso, l'array viene inizializzato per contenere 0s). Viene restituito l'indirizzo di questo blocco _array.

Secondo l'articolo, Common Language Specification (CLS) richiede che tutti gli array siano a base zero. Le matrici in .NET supportano matrici non basate su zero; tuttavia, questo è meno comune. Come risultato del "carattere comune" degli array a base zero, Microsoft ha trascorso molto tempo a ottimizzare le proprie prestazioni ; pertanto, gli array a dimensione singola, a base zero (SZ) sono "speciali" - e in realtà la migliore implementazione di un array (al contrario di multidimensionali, ecc.) - perché gli SZ hanno istruzioni linguistiche intermedie specifiche per manipolarli.

Le matrici vengono sempre passate per riferimento (come indirizzo di memoria), un pezzo importante del puzzle di matrice da conoscere. Mentre eseguono il controllo dei limiti (genererà un errore), il controllo dei limiti può anche essere disabilitato sugli array.

Ancora una volta, il più grande ostacolo alle matrici è che non sono ridimensionabili. Hanno una capacità "fissa". Presentazione di ArrayList and List (Of T) alla nostra storia:

ArrayList - elenco non generico

The ArrayList (insieme a List(Of T)- sebbene ci siano alcune differenze critiche, qui, spiegate più avanti) - è forse meglio pensato come la prossima aggiunta alle collezioni (in senso lato). ArrayList eredita dall'interfaccia IList (un discendente di 'ICollection'). Gli elenchi di array, a loro volta, sono più voluminosi - richiedono un overhead maggiore rispetto agli elenchi.

IListconsente all'implementazione di trattare gli ArrayList come elenchi di dimensioni fisse (come gli Array); tuttavia, al di là della funzionalità aggiuntiva aggiunta da ArrayLists, non vi sono reali vantaggi nell'uso di ArrayList di dimensioni fisse poiché ArrayLists (su Array) in questo caso sono notevolmente più lenti.

Dalla mia lettura, ArrayLists non può essere frastagliato: "L'uso di array multidimensionali come elementi ... non è supportato". Ancora una volta, un altro chiodo nella bara di ArrayLists. ArrayLists non sono anche "digitato" - il che significa che, al di sotto di tutto, un ArrayList è semplicemente un array dinamico di oggetti: Object[]. Ciò richiede un sacco di boxe (implicito) e unboxing (esplicito) durante l'implementazione di ArrayLists, aggiungendo di nuovo al loro overhead.

Pensiero non comprovato: penso di ricordare di aver letto o di aver sentito da uno dei miei professori che ArrayLists è una specie di figlio concettuale bastardo del tentativo di spostarsi dalle matrici alle raccolte di tipo elenco, vale a dire mentre una volta era stato un grande miglioramento per le matrici, non sono più l'opzione migliore poiché sono stati fatti ulteriori sviluppi rispetto alle collezioni

Elenco (di T): ciò che ArrayList è diventato (e sperava di essere)

La differenza nell'uso della memoria è abbastanza significativa da dove un Elenco (di Int32) consumava il 56% di memoria in meno rispetto a un ArrayList contenente lo stesso tipo primitivo (8 MB contro 19 MB nella dimostrazione collegata del gentleman sopra: di nuovo, collegata qui ) - sebbene questo è un risultato aggravato dalla macchina a 64 bit. Questa differenza dimostra davvero due cose: primo (1), un "oggetto" di tipo Int32 in scatola (ArrayList) è molto più grande di un tipo primitivo Int32 puro (Elenco); secondo (2), la differenza è esponenziale a causa del funzionamento interno di una macchina a 64 bit.

Quindi, qual è la differenza e cos'è un elenco (di T) ? MSDN definisce un List(Of T)", un elenco fortemente tipizzato di oggetti a cui è possibile accedere tramite indice." L'importanza qui è il bit "fortemente tipizzato": un elenco (di T) "riconosce" i tipi e memorizza gli oggetti come loro tipo. Quindi, un Int32viene memorizzato come tipo Int32e non come Objecttipo. Questo elimina i problemi causati da boxe e unboxing.

MSDN specifica che questa differenza entra in gioco solo quando si memorizzano tipi primitivi e non tipi di riferimento. Anche la differenza si verifica davvero su larga scala: oltre 500 elementi. La cosa più interessante è che la documentazione MSDN riporta "È vantaggioso utilizzare l'implementazione specifica del tipo della classe List (Of T) invece di usare la classe ArrayList ...."

In sostanza, List (Of T) è ArrayList, ma migliore. È "l'equivalente generico" di ArrayList. Come ArrayList, non è garantito che vengano ordinati fino a quando non vengono ordinati (vai alla figura). List (Of T) ha anche alcune funzionalità aggiunte.


5

Sono d'accordo con la domanda: anch'io ho trovato (trovare?) Sconcertante la scelta, quindi ho deciso scientificamente di vedere quale struttura di dati è la più veloce (ho fatto il test usando VB, ma immagino che C # sarebbe lo stesso, dato che entrambe le lingue fare la stessa cosa a livello di CLR). Puoi vedere alcuni risultati di benchmarking da me condotti qui (c'è anche qualche discussione su quale tipo di dati è meglio usare in quali circostanze).


3

Sono spiegati abbastanza bene in intellisense. Digita System.Collections. o System.Collections.Generics (preferito) e otterrai un elenco e una breve descrizione di ciò che è disponibile.


3

Hashtables / Dictionaries sono prestazioni O (1), il che significa che le prestazioni non sono una funzione delle dimensioni. È importante saperlo.

EDIT: in pratica, la complessità temporale media per le ricerche Hashtable / Dictionary <> è O (1).


5
Non esiste "performance". La complessità dipende dal funzionamento. Ad esempio, se si inseriscono n elementi nel Dizionario <>, non sarà O (1) a causa del rimodellamento.
Ilya Ryzhenkov,

2
Cordiali saluti, anche con rifacimento, Dizionario è ancora O (1). Considera lo scenario appena prima che il Dizionario si espanda. La metà degli elementi, quelli aggiunti dall'ultima espansione, saranno stati sottoposti a hashing una volta. La metà del resto sarà sottoposta a hash due volte. La metà del resto da questo, tre volte, ecc. Il numero medio di operazioni di hashing eseguite su ciascun elemento sarà 1 + 1/2 + 1/4 + 1/8 ... = 2. La situazione immediatamente dopo l'espansione è essenzialmente la stessa, ma con ogni elemento che è stato sottoposto a hash una volta in più (quindi il conteggio hash medio è tre). Tutti gli altri scenari sono tra quelli.
supercat

3

Le raccolte generiche avranno prestazioni migliori rispetto alle loro controparti non generiche, soprattutto quando si ripetono molti elementi. Questo perché non si verificano più boxe e unboxing.


2

Una nota importante su Hashtable vs Dictionary per l'ingegneria commerciale sistematica ad alta frequenza: discussione sulla sicurezza dei thread

Hashtable è thread-safe per l'uso da parte di più thread. I membri statici pubblici del dizionario sono thread-safe, ma non è garantito che tutti i membri di istanza lo siano.

Quindi Hashtable rimane la scelta 'standard' in questo senso.


Questo è parzialmente vero. Il Hashtableè sicuro da usare con un solo scrittore e lettori multipli contemporaneamente. D'altra parte, è sicuro utilizzare il Dictionarycon più lettori purché non venga modificato contemporaneamente.
Bryan Menard,

Decisamente. Nello spazio di trading, tuttavia, stiamo leggendo contemporaneamente da dati di mercato dal vivo e eseguendo analisi che includono le voci allegate. Dipende anche da quanti trader stanno utilizzando il sistema - se sei solo tu, ovviamente non importa.
Rob,

1
.NET 4.0 fornisce un ConcurrentDictionary <TKey, TValue>
Rob,

1

Esistono differenze sottili e non così sottili tra raccolte generiche e non generiche. Utilizzano semplicemente diverse strutture di dati sottostanti. Ad esempio, Hashtable garantisce uno scrittore-molti-lettori senza sincronizzazione. Dizionario no.


1

Strutture e raccolte di dati C # più popolari

  • Vettore
  • Lista di array
  • Elenco
  • Lista collegata
  • Dizionario
  • HashSet
  • Pila
  • Coda
  • SortedList

C # .NET ha molte strutture di dati diverse, ad esempio una delle più comuni è una matrice. Tuttavia, C # include molte più strutture di dati di base. La scelta della struttura dati corretta da utilizzare fa parte della stesura di un programma ben strutturato ed efficiente.

In questo articolo esaminerò le strutture di dati C # integrate, comprese le nuove introdotte in C # .NET 3.5. Si noti che molte di queste strutture dati si applicano ad altri linguaggi di programmazione.

Vettore

La struttura dei dati forse più semplice e più comune è l'array. L'array AC # è fondamentalmente un elenco di oggetti. Le sue caratteristiche distintive sono che tutti gli oggetti sono dello stesso tipo (nella maggior parte dei casi) e ne esiste un numero specifico. La natura di un array consente un accesso molto rapido agli elementi in base alla loro posizione all'interno dell'elenco (altrimenti noto come indice). L'array AC # è definito in questo modo:

[object type][] myArray = new [object type][number of elements]

Qualche esempio:

 int[] myIntArray = new int[5];
 int[] myIntArray2 = { 0, 1, 2, 3, 4 };

Come puoi vedere dall'esempio sopra, un array può essere inizializzato senza elementi o da un insieme di valori esistenti. L'inserimento di valori in un array è semplice purché si adattino. L'operazione diventa costosa quando ci sono più elementi della dimensione dell'array, a quel punto l'array deve essere espanso. Ciò richiede più tempo perché tutti gli elementi esistenti devono essere copiati nel nuovo array più grande.

Lista di array

La struttura di dati C #, ArrayList, è un array dinamico. Ciò significa che un ArrayList può avere qualsiasi quantità di oggetti e di qualsiasi tipo. Questa struttura di dati è stata progettata per semplificare i processi di aggiunta di nuovi elementi in un array. Sotto il cofano, un ArrayList è un array le cui dimensioni vengono raddoppiate ogni volta che si esaurisce lo spazio. Raddoppiare le dimensioni dell'array interno è una strategia molto efficace che riduce la quantità di elementi copiati nel lungo periodo. Non entreremo nella prova di questo qui. La struttura dei dati è molto semplice da usare:

    ArrayList myArrayList = new ArrayList();
    myArrayList.Add(56);
    myArrayList.Add("String");
    myArrayList.Add(new Form());

L'aspetto negativo della struttura di dati ArrayList è che è necessario riportare i valori recuperati nel loro tipo originale:

int arrayListValue = (int)myArrayList[0]

Fonti e altre informazioni che puoi trovare qui :


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.