Condividi tramite


Raccolte e strutture di dati

I dati simili possono spesso essere gestiti in modo più efficiente quando vengono archiviati e modificati come raccolta. È possibile usare la classe System.Array o le classi nei namespace System.Collections, System.Collections.Generic, System.Collections.Concurrent e System.Collections.Immutable per aggiungere, rimuovere e modificare singoli elementi o un intervallo di elementi in una raccolta.

Esistono due tipi principali di raccolte; raccolte generiche e raccolte non generiche. Le raccolte generiche sono sicure per i tipi in fase di compilazione. Per questo motivo, le raccolte generiche offrono in genere prestazioni migliori. Le raccolte generiche accettano un parametro di tipo quando vengono costruite. Non richiedono il cast da e verso il Object tipo quando si aggiungono o si rimuovono elementi dalla raccolta. Inoltre, la maggior parte delle raccolte generiche è supportata nelle app di Windows Store. Le raccolte non generiche archiviano gli elementi come Object, richiedono il cast e la maggior parte non sono supportate per lo sviluppo di app di Windows Store. Tuttavia, è possibile che nel codice precedente vengano visualizzate raccolte non generiche.

In .NET Framework 4 e nelle versioni successive, le raccolte del namespace System.Collections.Concurrent forniscono operazioni efficienti sicure per i thread per l'accesso agli elementi della raccolta da più thread. Le classi di raccolta non modificabili nello spazio dei nomi System.Collections.Immutable (pacchetto NuGet) sono intrinsecamente thread-safe perché le operazioni vengono eseguite su una copia di essa e la raccolta originale non può essere modificata.

Funzionalità comuni delle collezioni

Tutte le raccolte forniscono metodi per l'aggiunta, la rimozione o la ricerca di elementi nella raccolta. Inoltre, tutte le raccolte che implementano direttamente o indirettamente l'interfaccia ICollection o l'interfaccia ICollection<T> condividono queste funzionalità:

Inoltre, molte classi di raccolta contengono le funzionalità seguenti:

  • Proprietà Capacità e Conteggio

    La capacità di una raccolta è il numero di elementi che può contenere. Il conteggio di una raccolta è il numero di elementi effettivamente contenuti. Alcune raccolte nascondono la capacità o il conteggio o entrambi.

    La maggior parte delle raccolte si espande automaticamente in capacità quando si raggiunge la capacità corrente. La memoria viene riallocata e gli elementi vengono copiati dalla raccolta precedente a quella nuova. Questa progettazione riduce il codice necessario per usare la raccolta. Tuttavia, le prestazioni della raccolta potrebbero essere influenzate negativamente. Ad esempio, per List<T>, se Count è minore di Capacity, l'aggiunta di un elemento è un'operazione O(1). Se la capacità deve essere aumentata per contenere il nuovo elemento, l'aggiunta di un elemento diventa un'operazione O(n), dove n è Count. Il modo migliore per evitare prestazioni scarse causate da più riallocazioni consiste nell'impostare la capacità iniziale in modo che sia la dimensione stimata della raccolta.

    Un BitArray è un caso speciale; la sua capacità è la stessa della sua lunghezza, che corrisponde al conteggio.

  • Un limite inferiore coerente

    Il limite inferiore di una raccolta è l'indice del primo elemento. Tutte le raccolte indicizzate nei namespace System.Collections hanno un limite inferiore pari a zero, il che significa che sono indicizzate a partire da 0. Array ha un limite inferiore di zero per impostazione predefinita, ma è possibile definire un limite inferiore diverso quando si crea un'istanza della classe Array usando Array.CreateInstance.

  • Sincronizzazione per l'accesso da più thread (System.Collections solo classi).

    I tipi di raccolta non generici nello System.Collections spazio dei nomi forniscono alcune misure di thread safety con sincronizzazione, solitamente esposti attraverso i membri SyncRoot e IsSynchronized. Queste raccolte non sono thread-safe per impostazione predefinita. Se è necessario un accesso multithread scalabile ed efficiente a una raccolta, usare una delle classi nello spazio dei nomi System.Collections.Concurrent o considerare l'uso di una raccolta non modificabile. Per altre informazioni, vedere raccolteThread-Safe.

Scegliere una raccolta

In generale, è consigliabile usare raccolte generiche. Nella tabella seguente vengono descritti alcuni scenari comuni di raccolta e le classi di raccolta che è possibile usare per tali scenari. Se non si ha familiarità con le raccolte generiche, la tabella seguente consente di scegliere la raccolta generica più adatta per l'attività:

Vorrei... Opzioni di raccolta generica Opzioni di raccolta non generica Opzioni di raccolta thread-safe o non modificabili
Archiviare gli elementi come coppie chiave/valore per una ricerca rapida in base alla chiave Dictionary<TKey,TValue> Hashtable

Raccolta di coppie chiave/valore organizzate in base al codice hash della chiave.
ConcurrentDictionary<TKey,TValue>

ReadOnlyDictionary<TKey,TValue>

ImmutableDictionary<TKey,TValue>
Accedere agli elementi in base all'indice List<T> Array

ArrayList
ImmutableList<T>

ImmutableArray
Usare gli elementi in modalità 'primo entro, primo fuori' (FIFO) Queue<T> Queue ConcurrentQueue<T>

ImmutableQueue<T>
Usare i dati last-in-First-Out (LIFO) Stack<T> Stack ConcurrentStack<T>

ImmutableStack<T>
Accedere agli elementi in sequenza LinkedList<T> Nessuna raccomandazione Nessuna raccomandazione
Ricevere notifiche quando gli elementi vengono rimossi o aggiunti alla raccolta. (implementa INotifyPropertyChanged e INotifyCollectionChanged) ObservableCollection<T> Nessuna raccomandazione Nessuna raccomandazione
Raccolta ordinata SortedList<TKey,TValue> SortedList ImmutableSortedDictionary<TKey,TValue>

ImmutableSortedSet<T>
Set per le funzioni matematiche HashSet<T>

SortedSet<T>
Nessuna raccomandazione ImmutableHashSet<T>

ImmutableSortedSet<T>

Complessità algoritmica delle raccolte

Quando si sceglie una classe di raccolta, vale la pena considerare potenziali compromessi nelle prestazioni. Usare la tabella seguente per fare riferimento al confronto tra i vari tipi di raccolta modificabili nella complessità algoritmica con le corrispondenti controparti non modificabili. Spesso i tipi di raccolta non modificabili sono meno efficienti, ma offrono immutabilità, che spesso è un vantaggio comparativo valido.

Modificabile Ammortizzato Caso peggiore Immutabile Complessità
Stack<T>.Push O(1) O(n) ImmutableStack<T>.Push O(1)
Queue<T>.Enqueue O(1) O(n) ImmutableQueue<T>.Enqueue O(1)
List<T>.Add O(1) O(n) ImmutableList<T>.Add O(log n)
List<T>.Item[Int32] O(1) O(1) ImmutableList<T>.Item[Int32] O(log n)
List<T>.Enumerator O(n) O(n) ImmutableList<T>.Enumerator O(n)
HashSet<T>.AddRicerca O(1) O(n) ImmutableHashSet<T>.Add O(log n)
SortedSet<T>.Add O(log n) O(n) ImmutableSortedSet<T>.Add O(log n)
Dictionary<T>.Add O(1) O(n) ImmutableDictionary<T>.Add O(log n)
Dictionary<T> Ricerca O(1) O(1) – o rigorosamente O(n) ImmutableDictionary<T> Ricerca O(log n)
SortedDictionary<T>.Add O(log n) O(n log n) ImmutableSortedDictionary<T>.Add O(log n)

Un oggetto List<T> può essere enumerato in modo efficiente usando un ciclo for o un ciclo foreach. Un ImmutableList<T>oggetto , tuttavia, esegue un processo non ottimale all'interno di un for ciclo, a causa del tempo di O(log n) per il relativo indicizzatore. Enumerare un ImmutableList<T> utilizzando un ciclo foreach è efficiente perché ImmutableList<T> utilizza un albero binario per archiviare i dati anziché una matrice come fa List<T>. Una matrice può essere indicizzata rapidamente, mentre un albero binario deve essere percorso fino a quando non viene trovato il nodo con l'indice desiderato.

Inoltre, SortedSet<T> ha la stessa complessità di ImmutableSortedSet<T> perché entrambi usano alberi binari. La differenza significativa è che ImmutableSortedSet<T> usa un albero binario non modificabile. Poiché ImmutableSortedSet<T> offre anche una classe che consente la System.Collections.Immutable.ImmutableSortedSet<T>.Builder mutazione, è possibile avere sia immutabilità che prestazioni.

Titolo Descrizione
Selezione di una classe di raccolta Descrive le diverse raccolte e consente di selezionare una per lo scenario.
Tipi di raccolta di uso comune Descrive i tipi di raccolta generici e non generici di uso comune, come System.Array, System.Collections.Generic.List<T> e System.Collections.Generic.Dictionary<TKey,TValue>.
Quando usare raccolte generiche Viene illustrato l'uso di tipi di raccolta generici.
Confronti e ordinamenti all'interno di raccolte Illustra l'uso di confronti di uguaglianza e confronti di ordinamento nelle raccolte.
Tipi di raccolta ordinati Vengono descritte le prestazioni e le caratteristiche delle raccolte ordinate.
Tipi di raccolta Hashtable e Dictionary Vengono descritte le funzionalità dei tipi di dizionario generici e non generici basati su hash.
RaccolteThread-Safe Descrive i tipi di raccolta, System.Collections.Concurrent.BlockingCollection<T> ad esempio e System.Collections.Concurrent.ConcurrentBag<T> che supportano l'accesso simultaneo sicuro ed efficiente da più thread.
System.Collections.Immutable Introduce le raccolte non modificabili e fornisce collegamenti ai tipi di raccolta.

Riferimenti