List<T> ist eine geordnete Collection, die wächst und schrumpft, wenn du Elemente hinzufügst und entfernst. Sie liegt in System.Collections.Generic, wird wie ein Array indiziert (list[0]) und ist in C# die Standardwahl für „ein paar Dinge“.
Eine Liste erzeugen und Elemente hinzufügen
Der Typparameter in spitzen Klammern sagt, was die Liste enthält: List<string>, List<int>, List<Order>.
Ausgabe:
2
Zoe, Ana, Ben, Chloe, Dev
Ana
Ana B.
3
Count ist eine Property, keine Methode (Arrays verwenden Length, LINQ hat eine Methode Count(); bei einer Liste nimm die Property). Insert schiebt jedes spätere Element eine Position weiter, Einfügen am Anfang einer großen Liste kostet also Zeit proportional zu ihrer Größe. Add am Ende ist die günstige Operation.
Elemente entfernen
Es gibt vier Wege zu entfernen, je nachdem, was du über das Element weißt:
Ausgabe:
True: 85, 62, 40, 91, 55
False
62, 40, 91, 55
2 removed: 62, 91
Caught ArgumentOutOfRangeException
0
Remove nimmt einen Wert und löscht nur den ersten Treffer. Um alle Kopien eines Werts zu löschen, nimm RemoveAll(s => s == 40). Ein ungültiger Index wirft bei einer Liste ArgumentOutOfRangeException (ein Array wirft stattdessen IndexOutOfRangeException), sowohl bei list[i] als auch bei RemoveAt(i). RemoveRange(index, count) entfernt einen Block.
Count gegenüber Capacity
Eine Liste hält ihre Elemente in einem internen Array. Capacity ist die Größe dieses Arrays; Count ist die Anzahl der belegten Plätze. Erreicht Count den Wert Capacity, allokiert das nächste Add ein doppelt so großes Array und kopiert alles hinüber.
Ausgabe:
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
Durch das Verdoppeln ist die meisten Add-Aufrufe nur ein Schreiben in einen freien Platz. Wenn du ungefähr weißt, wie viele Elemente kommen, übergib die Zahl an den Konstruktor, um die Zwischenkopien zu sparen. Beachte, dass new List<int>(1000) trotzdem Count 0 hat: list[0] wirft darauf. Kapazität ist Platz, keine Elemente.
Suchen: Contains, IndexOf, Find, Exists
Ausgabe:
True
2
-1
32.00
2 cheap items
False
2
True
False
Find gibt den ersten Treffer zurück oder den Standardwert des Typs (null bei einer Klasse), wenn nichts passt, prüfe also vor der Verwendung des Ergebnisses. Die letzte Zeile gibt False aus, weil Contains Equals verwendet, und eine Klasse ohne überschriebenes Equals vergleicht Referenzen: Ein neues Product mit denselben Feldern ist ein anderes Objekt. Suche stattdessen mit Exists oder Find nach einer Property, oder überschreibe Equals und GetHashCode in der Klasse.
Jede dieser Methoden durchläuft die Liste von Anfang an, jeder Aufruf ist also O(n). Für wiederholte Nachschlagevorgänge über einen Schlüssel antwortet ein Dictionary in konstanter Zeit.
Eine Liste sortieren
Sort() sortiert an Ort und Stelle nach der natürlichen Ordnung der Elemente. Übergib ein Vergleichs-Lambda, um nach etwas anderem zu sortieren.
Ausgabe:
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
Ein Vergleich gibt eine negative Zahl zurück, wenn a zuerst kommen soll, eine positive, wenn b zuerst kommen soll, und null bei Gleichstand; CompareTo liefert genau das. Zwei Unterschiede zwischen Sort und OrderBy aus LINQ zählen in der Praxis:
Sortändert die Liste;OrderBygibt eine neue Sequenz zurück und lässt die Liste in Ruhe.Sortist nicht stabil: Elemente, die als gleich gelten, können die Plätze tauschen.OrderByist stabil, undThenByfügt einen zweiten Schlüssel hinzu:players.OrderBy(p => p.Score).ThenBy(p => p.Name).
Sort() auf einer Liste deiner eigenen Klasse ohne Vergleich aufzurufen wirft InvalidOperationException, weil die Liste nicht weiß, wie sie die Elemente ordnen soll. Übergib entweder einen Vergleich oder implementiere IComparable<T> in der Klasse.
Reverse() kehrt an Ort und Stelle um. Weil System.Linq ebenfalls eine Erweiterung Reverse definiert, wählt list.Reverse() auf einer List<T> die Version an Ort und Stelle, die nichts zurückgibt.
Durchlaufen und Entfernen beim Iterieren
foreach liest jedes Element der Reihe nach. Eine for-Schleife gibt dir zusätzlich den Index. Was du nicht tun kannst, ist mitten in einem foreach über dieselbe Liste Elemente hinzuzufügen oder zu entfernen:
Ausgabe:
Caught InvalidOperationException
120, 80, 60
120, 80, 60
Die Exception ist eine InvalidOperationException mit der Meldung „Collection was modified; enumeration operation may not execute.“ Eine vorwärts laufende for-Schleife wirft nicht, überspringt aber stillschweigend das Element direkt nach jedem entfernten, weil alles um einen Index nach vorn rückt. Rückwärts zu laufen vermeidet das. Eine dritte Lösung ist, über eine Kopie zu iterieren: foreach (var t in orders.ToList()).
Eine Property eines Elements in foreach zu ändern (zum Beispiel order.Status = "sent") ist in Ordnung. Hinzufügen oder Entfernen macht den Enumerator kaputt, ebenso das Ersetzen eines Elements über den Indexer (orders[i] = 0).
Zwischen Listen und Arrays umwandeln
Ausgabe:
4
3
4, 4, 4, 4
Oslo / Lima / Pune / Kyiv
Beide Richtungen kopieren die Elemente in neuen Speicher, eine spätere Änderung der Liste wirkt sich also nicht auf das Array aus. ConvertAll ist die eigene Version der Liste von Select(...).ToList() aus LINQ. Eine Liste mit Console.WriteLine(list) auszugeben zeigt den Typnamen (System.Collections.Generic.List`1[System.String]); nimm string.Join.
Kurzübersicht
| Aufgabe | Code |
|---|---|
| Erzeugen | var l = new List<int>(); oder new List<int> { 1, 2 } |
| Am Ende hinzufügen | l.Add(x), l.AddRange(items) |
| An einer Position einfügen | l.Insert(i, x) |
| Ersten Treffer entfernen | l.Remove(x) (gibt bool zurück) |
| Nach Index entfernen | l.RemoveAt(i) |
| Nach Bedingung entfernen | l.RemoveAll(x => ...) (gibt Anzahl zurück) |
| Anzahl der Elemente | l.Count |
| Enthält | l.Contains(x), l.Exists(x => ...) |
| Finden | l.Find(...), l.FindAll(...), l.FindIndex(...) |
| An Ort und Stelle sortieren | l.Sort(), l.Sort((a, b) => ...) |
| Sortierte Kopie | l.OrderBy(x => ...).ToList() |
| In Array | l.ToArray() |
Häufige Fehler
- In
foreachentfernen. WirftInvalidOperationException; nimmRemoveAlloder eine rückwärts laufendefor-Schleife. - Erwarten, dass
Remove(x)jede Kopie entfernt. Es entfernt die erste; nimmRemoveAll. Containsauf Objekten ohneEqualsverwenden. Es vergleicht Referenzen; suche mitExistsnach einer Property.- In eine mit Kapazität erzeugte Liste indizieren.
new List<int>(10)ist leer; füge zuerst Elemente hinzu. - Annehmen, dass
SortGleichstände in der Reihenfolge lässt. Tut es nicht; nimmOrderBy, wenn die Reihenfolge unter Gleichen zählt. - Eine Liste zuweisen, um sie zu teilen, und dann von Änderungen überrascht sein.
var copy = list;ist dieselbe Liste;new List<T>(list)ist eine Kopie.
Häufig gestellte Fragen
Wie füge ich in C# Elemente zu einer List hinzu?
list.Add(item) hängt ein Element ans Ende, list.AddRange(otherCollection) hängt viele an, und list.Insert(index, item) setzt ein Element an eine Position und schiebt den Rest weiter. Du kannst eine Liste auch beim Erzeugen füllen: var names = new List<string> { "Ana", "Ben" };.
Wie entferne ich in C# ein Element aus einer List?
Remove(value) entfernt das erste passende Element und gibt true zurück, wenn es eines gefunden hat. RemoveAt(index) entfernt nach Position. RemoveAll(x => condition) entfernt jedes passende Element und gibt zurück, wie viele es entfernt hat. Clear() leert die Liste.
Wie prüfe ich in C#, ob eine List einen Wert enthält?
list.Contains(value) gibt true oder false zurück. Es vergleicht mit Equals, bei deinen eigenen Klassen also Referenzen, außer die Klasse überschreibt Equals. Um nach einer Property zu prüfen, nimm list.Exists(p => p.Name == "Ana") oder list.Any(...) aus LINQ.
Wie sortiere ich in C# eine List nach einer Property?
Übergib Sort einen Vergleich: people.Sort((a, b) => a.Age.CompareTo(b.Age)) sortiert an Ort und Stelle. Vertausche a und b für absteigende Reihenfolge. Oder nimm LINQ, das eine neue sortierte Sequenz zurückgibt und gleiche Elemente in ihrer ursprünglichen Reihenfolge lässt: people.OrderBy(p => p.Age).ToList().
Warum bekomme ich „Collection was modified; enumeration operation may not execute“?
Du hast Elemente hinzugefügt, entfernt oder ersetzt (list[i] = x), während ein foreach über dieselbe Liste lief, und der Enumerator wirft beim nächsten Schritt InvalidOperationException. Nimm list.RemoveAll(condition), durchlaufe die Liste rückwärts mit einer for-Schleife oder iteriere über eine Kopie wie list.ToList().
Was ist der Unterschied zwischen Count und Capacity?
Count ist die Anzahl der Elemente in der Liste. Capacity ist die Größe des internen Arrays, die mindestens Count beträgt. Wenn das Array voll ist, allokiert die Liste ein neues, doppelt so großes und kopiert die Elemente, daher ist Add im Durchschnitt schnell.