List<T> è una collezione ordinata che cresce e si riduce man mano che aggiungi e rimuovi elementi. Si trova in System.Collections.Generic, si indicizza come un array (list[0]) ed è la scelta predefinita in C# per "un insieme di cose".
Creare una lista e aggiungere elementi
Il parametro di tipo tra parentesi angolari dice cosa contiene la lista: List<string>, List<int>, List<Order>.
Output:
2
Zoe, Ana, Ben, Chloe, Dev
Ana
Ana B.
3
Count è una proprietà, non un metodo (gli array usano Length, LINQ ha un metodo Count(); su una lista usa la proprietà). Insert sposta in avanti di una posizione ogni elemento successivo, quindi inserire in testa a una lista grande richiede un tempo proporzionale alla sua dimensione. Add in fondo è l'operazione economica.
Rimuovere elementi
Ci sono quattro modi per rimuovere, a seconda di cosa sai dell'elemento:
Output:
True: 85, 62, 40, 91, 55
False
62, 40, 91, 55
2 removed: 62, 91
Caught ArgumentOutOfRangeException
0
Remove accetta un valore ed elimina solo la prima corrispondenza. Per eliminare tutte le copie di un valore, usa RemoveAll(s => s == 40). Un indice non valido su una lista lancia ArgumentOutOfRangeException (un array lancia invece IndexOutOfRangeException), sia per list[i] sia per RemoveAt(i). RemoveRange(index, count) rimuove un blocco.
Count e Capacity
Una lista tiene i suoi elementi in un array interno. Capacity è la dimensione di quell'array; Count è quanti posti sono occupati. Quando Count raggiunge Capacity, il successivo Add alloca un array grande il doppio e ci copia tutto.
Output:
Count 0, Capacity 0
Count 1, Capacity 4
Count 2, Capacity 4
Count 3, Capacity 4
Count 4, Capacity 4
Count 5, Capacity 8
Count 6, Capacity 8
Count 7, Capacity 8
Count 8, Capacity 8
Count 9, Capacity 16
Count 0, Capacity 1000
Raddoppiare significa che la maggior parte delle chiamate ad Add è solo una scrittura in un posto libero. Se sai più o meno quanti elementi arriveranno, passa il numero al costruttore per evitare le copie intermedie. Nota che new List<int>(1000) ha comunque Count 0: list[0] su di essa lancia un'eccezione. La capacità è spazio, non elementi.
Cercare: Contains, IndexOf, Find, Exists
Output:
True
2
-1
32.00
2 cheap items
False
2
True
False
Find restituisce la prima corrispondenza oppure il valore predefinito del tipo (null per una classe) quando non trova nulla, quindi controlla il risultato prima di usarlo. L'ultima riga stampa False perché Contains usa Equals, e una classe senza un override di Equals confronta i riferimenti: un nuovo Product con gli stessi campi è un oggetto diverso. Cerca invece per proprietà con Exists o Find, oppure sovrascrivi Equals e GetHashCode nella classe.
Ognuno di questi metodi percorre la lista dall'inizio, quindi ogni chiamata è O(n). Per ricerche ripetute in base a una chiave, un Dictionary risponde in tempo costante.
Ordinare una lista
Sort() ordina sul posto usando l'ordine naturale degli elementi. Passa una lambda di confronto per ordinare in base a qualsiasi altra cosa.
Output:
1, 2, 5, 9
9, 5, 2, 1
Ben 95, Ana 120, Chloe 150
Chloe 150, Ben 95, Ana 120
Ben 95, Ana 120, Chloe 150
Un confronto restituisce un numero negativo quando a deve venire prima, positivo quando deve venire prima b, e zero in caso di parità; CompareTo produce esattamente questo. Due differenze tra Sort e l'OrderBy di LINQ contano nella pratica:
Sortmodifica la lista;OrderByrestituisce una nuova sequenza e lascia stare la lista.Sortnon è stabile: gli elementi che risultano uguali possono scambiarsi di posto.OrderByè stabile, eThenByaggiunge una seconda chiave:players.OrderBy(p => p.Score).ThenBy(p => p.Name).
Chiamare Sort() su una lista di una tua classe senza un confronto lancia InvalidOperationException, perché la lista non sa come ordinare gli elementi. Passa un confronto oppure implementa IComparable<T> nella classe.
Reverse() inverte sul posto. Dato che anche System.Linq definisce un'estensione Reverse, list.Reverse() su una List<T> sceglie la versione sul posto, che non restituisce nulla.
Scorrere la lista e rimuovere durante l'iterazione
foreach legge ogni elemento in ordine. Un ciclo for ti dà anche l'indice. Quello che non puoi fare è aggiungere o rimuovere elementi nel mezzo di un foreach sulla stessa lista:
Output:
Caught InvalidOperationException
120, 80, 60
120, 80, 60
L'eccezione è InvalidOperationException con il messaggio "Collection was modified; enumeration operation may not execute." Un ciclo for in avanti non lancia eccezioni, ma salta in silenzio l'elemento subito dopo ogni rimozione, perché tutto scala indietro di un indice. Scorrere all'indietro evita il problema. Una terza soluzione è iterare su una copia: foreach (var t in orders.ToList()).
Modificare una proprietà di un elemento dentro foreach (per esempio order.Status = "sent") va bene. Aggiungere o rimuovere elementi rompe l'enumeratore, e lo stesso fa sostituirne uno tramite l'indicizzatore (orders[i] = 0).
Convertire tra liste e array
Output:
4
3
4, 4, 4, 4
Oslo / Lima / Pune / Kyiv
Entrambe le direzioni copiano gli elementi in una nuova area di memoria, quindi modificare la lista in seguito non influisce sull'array. ConvertAll è la versione propria della lista di Select(...).ToList() di LINQ. Stampare una lista con Console.WriteLine(list) mostra il nome del tipo (System.Collections.Generic.List`1[System.String]); usa string.Join.
Riferimento rapido
| Operazione | Codice |
|---|---|
| Creare | var l = new List<int>(); o new List<int> { 1, 2 } |
| Aggiungere in fondo | l.Add(x), l.AddRange(items) |
| Inserire in una posizione | l.Insert(i, x) |
| Rimuovere la prima corrispondenza | l.Remove(x) (restituisce bool) |
| Rimuovere per indice | l.RemoveAt(i) |
| Rimuovere per condizione | l.RemoveAll(x => ...) (restituisce il conteggio) |
| Numero di elementi | l.Count |
| Contiene | l.Contains(x), l.Exists(x => ...) |
| Trovare | l.Find(...), l.FindAll(...), l.FindIndex(...) |
| Ordinare sul posto | l.Sort(), l.Sort((a, b) => ...) |
| Copia ordinata | l.OrderBy(x => ...).ToList() |
| In array | l.ToArray() |
Errori comuni
- Rimuovere dentro un
foreach. LanciaInvalidOperationException; usaRemoveAllo un cicloforall'indietro. - Aspettarsi che
Remove(x)rimuova ogni copia. Rimuove la prima; usaRemoveAll. - Usare
Containssu oggetti senzaEquals. Confronta i riferimenti; cerca per proprietà conExists. - Accedere per indice a una lista creata con una capacità.
new List<int>(10)è vuota; aggiungi prima gli elementi. - Dare per scontato che
Sortmantenga l'ordine in caso di parità. Non lo fa; usaOrderByquando conta l'ordine tra elementi uguali. - Assegnare una lista per condividerla, e poi sorprendersi delle modifiche.
var copy = list;è la stessa lista;new List<T>(list)è una copia.
Domande frequenti
Come aggiungo elementi a una List in C#?
list.Add(item) aggiunge un elemento in fondo, list.AddRange(otherCollection) ne aggiunge molti, e list.Insert(index, item) mette un elemento in una posizione e sposta in avanti gli altri. Puoi anche riempire una lista mentre la crei: var names = new List<string> { "Ana", "Ben" };.
Come rimuovo un elemento da una List in C#?
Remove(value) rimuove il primo elemento corrispondente e restituisce true se ne ha trovato uno. RemoveAt(index) rimuove per posizione. RemoveAll(x => condition) rimuove ogni elemento che soddisfa la condizione e restituisce quanti ne ha rimossi. Clear() svuota la lista.
Come controllo se una List contiene un valore in C#?
list.Contains(value) restituisce true o false. Confronta con Equals, quindi per le tue classi confronta i riferimenti a meno che la classe non sovrascriva Equals. Per controllare in base a una proprietà, usa list.Exists(p => p.Name == "Ana") oppure list.Any(...) di LINQ.
Come ordino una List per proprietà in C#?
Passa un confronto a Sort: people.Sort((a, b) => a.Age.CompareTo(b.Age)) ordina sul posto. Scambia a e b per l'ordine decrescente. Oppure usa LINQ, che restituisce una nuova sequenza ordinata e mantiene gli elementi uguali nel loro ordine originale: people.OrderBy(p => p.Age).ToList().
Perché ottengo "Collection was modified; enumeration operation may not execute"?
Hai aggiunto, rimosso o sostituito (list[i] = x) elementi mentre un foreach stava iterando sulla stessa lista, e l'enumeratore lancia InvalidOperationException al passo successivo. Usa list.RemoveAll(condition), scorri all'indietro con un ciclo for, oppure itera su una copia come list.ToList().
Che differenza c'è tra Count e Capacity?
Count è il numero di elementi nella lista. Capacity è la dimensione dell'array interno, che è almeno pari a Count. Quando l'array si riempie, la lista ne alloca uno nuovo grande il doppio e copia gli elementi, quindi Add è veloce in media.