Kollektionen in C#: Liste, Wörterbuch, Warteschlange, Stapel
Lernen Sie Collections in C# wie List, Dictionary, Queue und Stack kennen, um Daten effizient zu verwalten – mit Beispielen.
In C# werden Kollektionsklassen verwendet, um mehrere Werte dynamisch zu speichern und effizient zu verarbeiten.
Arrays haben eine feste Größe, aber Kollektionen können bei Bedarf wachsen oder schrumpfen.
In diesem Artikel werden List<T>, Dictionary<TKey,TValue> (und KeyValuePair<,>),
SortedList<TKey,TValue>, Queue<T>, Stack<T>,
HashSet<T> und LinkedList<T> mit Beispielen behandelt.
List<T>
List<T> ist eine dynamisch vergrößerbare Liste, die Elemente desselben Typs in Reihenfolge speichert.
Sie bietet umfangreiche Methoden zum Hinzufügen/Entfernen, Suchen und Sortieren.
using System;
using System.Collections.Generic;
using System.Linq;
var zahlen = new List<int> { 5, 1, 9 };
zahlen.Add(3); // am Ende hinzufügen
zahlen.Insert(1, 7); // an bestimmtem Index einfügen
zahlen.Remove(9); // nach Wert entfernen (erstes Vorkommen)
zahlen.RemoveAll(x => x % 2 == 1); // alle ungeraden Zahlen entfernen
zahlen.AddRange(new[] { 10, 2, 8 });
zahlen.Sort(); // aufsteigend sortieren
zahlen.Reverse(); // Reihenfolge umkehren
Console.WriteLine(string.Join(", ", zahlen));
Console.WriteLine(zahlen.Contains(8)); // true
// LINQ-Filterung
var gerade = zahlen.Where(n => n % 2 == 0).ToList();
Console.WriteLine(string.Join(", ", gerade));
Dictionary<TKey,TValue> und KeyValuePair<TKey,TValue>
Dictionary speichert Schlüssel–Wert-Paare. Schlüssel sind eindeutig.
Es ist hash-basiert und bietet sehr schnelle Zugriffe.
using System;
using System.Collections.Generic;
var preise = new Dictionary<string, decimal>(StringComparer.OrdinalIgnoreCase)
{
["Apfel"] = 5.75m,
["Birne"] = 7.25m
};
// Hinzufügen oder aktualisieren
preise["Banane"] = 12m; // fügt hinzu, wenn nicht vorhanden, aktualisiert sonst
// Sicheres Lesen
if (preise.TryGetValue("APFEL", out var preis))
Console.WriteLine($"Apfel: {preis} EUR");
// Schlüsselprüfung
if (!preise.ContainsKey("Kirsche"))
preise.Add("Kirsche", 18.5m);
// Iteration mit KeyValuePair
foreach (KeyValuePair<string, decimal> kv in preise)
Console.WriteLine($"{kv.Key} → {kv.Value} EUR");
// Wert aktualisieren
preise["Apfel"] = preise["Apfel"] * 1.10m; // +10% Preis
Tipp: KeyValuePair<TKey,TValue> stellt beim Iterieren .Key und .Value bereit.
Zum Aktualisieren wird direkt dict[key] verwendet.
SortedList<TKey,TValue>
SortedList hält Schlüssel aufsteigend sortiert und erlaubt indexbasierten Zugriff über Keys und Values.
Sie kombiniert Eigenschaften von Dictionary und Array.
using System;
using System.Collections.Generic;
var preisliste = new SortedList<string, decimal>(StringComparer.OrdinalIgnoreCase)
{
["Birne"] = 7.25m,
["Apfel"] = 5.75m,
["Banane"] = 12m,
["Kirsche"] = 18.5m
};
// Sortierte Schlüssel:
Console.WriteLine(string.Join(", ", preisliste.Keys)); // Apfel, Banane, Birne, Kirsche
// Indexbasierter Zugriff:
for (int i = 0; i < preisliste.Count; i++)
Console.WriteLine($"#{i} {preisliste.Keys[i]} → {preisliste.Values[i]}");
// Benutzerdefinierter Comparer (absteigend):
var absteigend = new SortedList<string, decimal>(Comparer<string>.Create((a,b) => StringComparer.OrdinalIgnoreCase.Compare(b,a)));
absteigend["Apfel"] = 5.75m;
absteigend["Birne"] = 7.25m;
Console.WriteLine(string.Join(", ", absteigend.Keys)); // umgekehrte Reihenfolge
SortedListverschiebt beim Einfügen/Entfernen Elemente, um die Reihenfolge zu erhalten. Bei vielen Änderungen lieberSortedDictionarynutzen.- Wenn Indexzugriffe auf Bereiche benötigt werden, ist
SortedListvorteilhaft.
Queue<T> (FIFO)
Queue arbeitet nach dem First In, First Out-Prinzip; nützlich für Workflows und Warteschlangen.
using System;
using System.Collections.Generic;
var bestellungen = new Queue<string>();
bestellungen.Enqueue("Bestellung#1001");
bestellungen.Enqueue("Bestellung#1002");
bestellungen.Enqueue("Bestellung#1003");
Console.WriteLine(bestellungen.Peek()); // nächste ohne Entfernen: Bestellung#1001
Console.WriteLine(bestellungen.Dequeue()); // entfernt: Bestellung#1001
Console.WriteLine(bestellungen.Count); // 2
Stack<T> (LIFO)
Stack arbeitet nach dem Last In, First Out-Prinzip; genutzt für Undo, Verlauf usw.
using System;
using System.Collections.Generic;
var schritte = new Stack<string>();
schritte.Push("Schritt-1");
schritte.Push("Schritt-2");
schritte.Push("Schritt-3");
Console.WriteLine(schritte.Peek()); // Schritt-3
Console.WriteLine(schritte.Pop()); // Schritt-3 (entfernt)
Console.WriteLine(schritte.Pop()); // Schritt-2
HashSet<T>
HashSet speichert eine Sammlung eindeutiger Elemente. Doppelte Werte werden ignoriert.
Es ist ideal für Mengenoperationen (Vereinigung, Schnittmenge, Differenz).
using System;
using System.Collections.Generic;
var a = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "apfel", "birne", "banane" };
var b = new HashSet<string> { "Banane", "kirsche" };
var vereinigung = new HashSet<string>(a);
vereinigung.UnionWith(b); // A ∪ B
var schnitt = new HashSet<string>(a);
schnitt.IntersectWith(b); // A ∩ B
var differenz = new HashSet<string>(a);
differenz.ExceptWith(b); // A \ B
Console.WriteLine(string.Join(", ", vereinigung)); // apfel, birne, banane, kirsche
Console.WriteLine(string.Join(", ", schnitt)); // banane
Console.WriteLine(string.Join(", ", differenz)); // apfel, birne
Console.WriteLine(a.Add("Apfel")); // false (schon vorhanden, case-insensitive)
Tipp: Mitgliedschaftstests (Contains) und Einfügen sind in HashSet meist O(1) amortisiert; ideal zum Entfernen von Duplikaten in großen Datenmengen.
LinkedList<T>
LinkedList ist eine doppelt verkettete Liste. Einfügen/Entfernen an bekannten Knoten kostet O(1).
Zufälliger Indexzugriff ist nicht möglich; Traversieren ist erforderlich.
using System;
using System.Collections.Generic;
var liste = new LinkedList<string>();
var node1 = liste.AddFirst("Start");
var node2 = liste.AddLast("Ende");
liste.AddAfter(node1, "Mitte-1"); // Start <-> Mitte-1 <-> Ende
liste.AddBefore(node2, "Mitte-2"); // Start <-> Mitte-1 <-> Mitte-2 <-> Ende
// Knoten entfernen
var mitte1 = node1.Next;
liste.Remove(mitte1);
// Traversieren
for (var n = liste.First; n != null; n = n.Next)
Console.WriteLine(n.Value); // Start, Mitte-2, Ende
- Schnell für Kopf-/Ende-Operationen und knotengenaue Einfügungen/Entfernungen.
- Nicht geeignet für zufälligen Indexzugriff; dafür
List<T>nutzen.
Beispielanwendung: Bestellverarbeitungspipeline
Im folgenden Szenario werden verschiedene Kollektionen kombiniert: Queue für wartende Bestellungen, Dictionary für Lager und Preise, HashSet für eindeutige Kunden, Stack für Undo, SortedList für Preisliste und LinkedList für „zuletzt angesehene Produkte“.
using System;
using System.Collections.Generic;
var wartend = new Queue<string>(new[] { "BEST-1001", "BEST-1002" });
var lager = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
{
["Apfel"] = 100, ["Birne"] = 50, ["Banane"] = 30
};
var preise = new SortedList<string, decimal> { ["Birne"] = 7.25m, ["Apfel"] = 5.75m, ["Banane"] = 12m };
var kunden = new HashSet<string>(); // eindeutige Kunden-IDs
var undo = new Stack<Action>(); // Undo-Stack
var zuletzt = new LinkedList<string>(); // zuletzt angesehene Produkte
void InDenWarenkorb(string produkt, int menge)
{
if (lager.TryGetValue(produkt, out var s) && s >= menge)
{
lager[produkt] = s - menge;
undo.Push(() => lager[produkt] += menge); // Rückgängig-Aktion
zuletzt.AddFirst(produkt);
Console.WriteLine($"{menge} x {produkt} hinzugefügt. Verbleibend: {lager[produkt]}");
}
else
{
Console.WriteLine($"Nicht genügend Bestand: {produkt}");
}
}
void BestellungVerarbeiten(string bestellNr, string kundenId)
{
Console.WriteLine($"Verarbeite: {bestellNr}");
kunden.Add(kundenId);
InDenWarenkorb("Apfel", 3);
InDenWarenkorb("Birne", 2);
}
while (wartend.Count > 0)
{
var bestellung = wartend.Dequeue();
BestellungVerarbeiten(bestellung, "KUND-001");
}
Console.WriteLine($"Eindeutige Kunden: {kunden.Count}");
Console.WriteLine($"Preisliste (sortiert): {string.Join(", ", preise.Keys)}");
// Rückgängig machen falls nötig:
if (undo.Count > 0)
{
var rueck = undo.Pop();
rueck();
}
Console.WriteLine("Zuletzt angesehene (Top 3):");
int k = 0;
for (var n = zuletzt.First; n != null && k < 3; n = n.Next, k++)
Console.WriteLine($"- {n.Value}");
Kurz & Knapp
- List<T>: Dynamische, indexbasierte Liste. Ideal für Zufallszugriff.
- Dictionary<TKey,TValue>: Schlüssel–Wert-Paare, schnelle Suche. Mit KeyValuePair iterieren.
- SortedList<TKey,TValue>: Sortierte Schlüssel + Indexzugriff. Gut für Bereichsabfragen.
- Queue<T> (FIFO): Workflows/Warteschlangen.
- Stack<T> (LIFO): Undo, Verlauf.
- HashSet<T>: Eindeutige Elemente; Mengenoperationen (∪, ∩, \).
- LinkedList<T>: Knotenbasierte Liste; schnelle Kopf-/Ende-Einfügungen/Entfernungen.
Leitfaden: Zufallszugriff → List, schnelle Schlüsselsuche → Dictionary, sortierte Schlüssel + Index → SortedList, Eindeutigkeit → HashSet, FIFO → Queue, LIFO → Stack, schnelle Kopf-/Ende-Operationen → LinkedList.
Ähnliche Artikel
Arrays (Felder) in C#
Lernen Sie Arrays in C#, inklusive Deklaration, Indexzugriff, Schleifen und typischer Array-Operationen mit Beispielen.
C# Schleifen (for, foreach, while, do-while)
Lernen Sie die Verwendung von for-, foreach-, while- und do-while-Schleifen in C#, um wiederholte Abläufe effizient umzusetzen.
Generische Strukturen in C# (List<T>, Dictionary<TKey,TValue>)
Lernen Sie Generics in C# (List<T>, Dictionary<TKey,TValue>), um typsicheren, wiederverwendbaren Code zu schreiben.
Lambda-Ausdrücke in C#
Lernen Sie Lambda-Ausdrücke in C#, einschließlich Kurzsyntax, Func- und Action-Delegates und LINQ-Beispiele.