1. Einführung
Mal ehrlich, Arrays sind schon cool, aber sie haben ihre Grenzen. Muss die Größe geändert werden? Dann musst du ein neues Array anlegen und die Elemente von Hand kopieren! Willst du schnell ein Element per Schlüssel finden? Das Array sagt: "Na, versuch das mal selbst".
Das echte Leben ist voll von Szenarien, in denen Arrays einfach unpraktisch sind. Stell dir vor, du schreibst eine App zur Verwaltung von Büchern in einer Bibliothek (hi, unser kleines Lernsystem!). Am Anfang hast du 5 Bücher – da reicht ein Array. Nach einer Woche sind es schon 500, und nach einem Monat hat jemand ein Buch zurückgebracht, ein anderes ist verloren gegangen... Hier kommen Collections ins Spiel!
Collections sind Container, die folgendes können:
- Passen ihre Größe automatisch an.
- Bieten schnellen Zugriff, Hinzufügen und Entfernen.
- Bieten bequemere Möglichkeiten zur Datenverarbeitung: Sortierung, Filterung, Gruppenoperationen.
In .NET gibt es mehrere wichtige "Familien" von Collections. Heute schauen wir uns die wichtigsten an: Listen (List<T>), Dictionaries (Dictionary<TKey, TValue>), Sets (HashSet<T>), Queues, Stacks und auch die Interfaces, die dahinterstecken.
2. Die wichtigsten Collection-Typen
Auch wenn wir noch nicht tief in die einzelnen generischen Collection-Typen eingetaucht sind, ist es wichtig zu verstehen, welche Hauptkategorien von Collections es gibt und für welche Aufgaben sie geeignet sind. Stell dir das wie verschiedene Arten von "Containern" vor, jeder mit seinen eigenen Besonderheiten:
Listen (Lists)
Was ist das? Eine geordnete Folge von Elementen. Wie eine Einkaufsliste oder eine Liste von Studierenden im Klassenbuch.
Wichtige Merkmale: Elemente haben eine Reihenfolge (du kannst per Index darauf zugreifen, wie beim Array). Doppelte Elemente sind erlaubt. Die Größe ist dynamisch.
Wann verwenden? Wenn die Reihenfolge der Elemente wichtig ist und du Elemente schnell am Ende hinzufügen/entfernen oder per Position darauf zugreifen willst.
Beispiel aus dem Alltag: Warteschlange an der Kasse, Teilnehmerliste für ein Webinar, Abfolge von Frames in einer Animation.
Dictionaries/Maps
Was ist das? Eine Sammlung von "Schlüssel-Wert"-Paaren. Wie ein echtes Wörterbuch, bei dem jedes Wort (Schlüssel) eine Bedeutung (Wert) hat.
Wichtige Merkmale: Jeder Schlüssel ist einzigartig. Der Schlüssel wird für schnellen Zugriff auf den Wert verwendet. Die Reihenfolge der Elemente ist meistens nicht garantiert.
Wann verwenden? Wenn du schnell einen Wert anhand eines eindeutigen Identifikators (Schlüssel) finden willst.
Beispiel aus dem Alltag: Adressbuch (Name - Telefonnummer), Produktdatenbank (Produkt-ID - Beschreibung), App-Einstellungen (Einstellungsname - Wert).
Sets
Was ist das? Eine ungeordnete Sammlung von einzigartigen Elementen. Wie eine mathematische Menge.
Wichtige Merkmale: Keine doppelten Elemente erlaubt. Die Reihenfolge ist nicht garantiert. Optimiert für die Überprüfung, ob ein Element vorhanden ist, und für Mengenoperationen (Vereinigung, Schnittmenge).
Wann verwenden? Wenn du einzigartige Werte speichern und schnell prüfen willst, ob ein Element im Set ist.
Beispiel aus dem Alltag: Liste der einzigartigen Besucher einer Website, Tag-Set für einen Artikel, Wortliste für Autovervollständigung (ohne Duplikate).
Queues
Was ist das? Eine Collection, die nach dem Prinzip "First In – First Out" (FIFO) arbeitet.
Wichtige Merkmale: Elemente werden am Ende hinzugefügt und am Anfang entfernt.
Wann verwenden? Wenn du Prozesse modellierst, bei denen die Reihenfolge der Bearbeitung wichtig ist, z.B. Ticketsystem, Druckwarteschlange.
Stacks
Was ist das? Eine Collection, die nach dem Prinzip "Last In – First Out" (LIFO) arbeitet.
Wichtige Merkmale: Elemente werden nur an einem Ende (oben) hinzugefügt und entfernt.
Wann verwenden? Zum Nachverfolgen von Aktionen (Undo in Editoren), Verarbeitung verschachtelter Strukturen, Rekursion.
Unterschiede zwischen Collection-Typen
| Collection-Typ | Analogie | Grundprinzip | Zugriff per Index? | Element-Reihenfolge? | Duplikate erlaubt? | Wichtige Operationen |
|---|---|---|---|---|---|---|
| Array | Reihe nummerierter Zellen | Feste Größe | Ja | Ja | Ja | Zugriff/Setzen per Index |
| Liste | Einkaufsliste | Dynamisch, geordnet | Ja | Ja | Ja | Hinzufügen, Entfernen, Suchen |
| Dictionary | Wörterbuch (Schlüssel-Wert) | Einzigartige Schlüssel | Nein | Nein (meistens) | Nein (per Schlüssel) | Zugriff per Schlüssel, Hinzufügen |
| Set | Menge einzigartiger Objekte | Nur einzigartige Elemente | Nein | Nein | Nein | Prüfen auf Vorhandensein, Vereinigung |
| Queue | Warteschlange an der Kasse | FIFO (First In – First Out) | Nein | Ja | Ja | Hinzufügen ans Ende, Entfernen vom Anfang |
| Stack | Stapel Teller | LIFO (Last In – First Out) | Nein | Ja | Ja | Oben hinzufügen, oben entfernen |
3. Liste: List<T>
Die Liste ist die meistgenutzte Collection in C#. Sogar häufiger als das Array. Eine Liste ist eine Collection, die wie ein Array ist, aber von selbst wachsen kann. Du kannst Elemente hinzufügen und entfernen, ohne Kopfschmerzen zu bekommen.
using System;
using System.Collections.Generic;
var zahlen = new List<int>(); // Leere Liste von int anlegen
zahlen.Add(10); // Element hinzufügen
zahlen.Add(15);
zahlen.Add(42);
Console.WriteLine(zahlen[0]); // 10
zahlen.Remove(15); // Element per Wert entfernen
foreach (var zahl in zahlen)
{
Console.WriteLine(zahl);
}
// Gibt aus: 10 und 42
Warum ist List<T> besser als ein Array, wenn du eine Collection mit unbekannter Größe brauchst?
— Weil du nicht selbst mehr Speicher reservieren und das Array kopieren musst, wenn du Elemente hinzufügst – das macht die Collection für dich!
Wann List<T> verwenden?
- Dynamische Liste, wenn du Elemente hinzufügen, entfernen, ändern willst.
- Du brauchst keinen schnellen Zugriff per Schlüssel (das ist was für Dictionaries).
- Wird oft für geordnete Mengen verwendet.
4. Dictionary Dictionary<TKey, TValue>
Ein Array (und eine Liste) speichert Werte in Zellen, jede Zelle hat einen Index. Ein Dictionary ist eine Collection, bei der du statt einer Zahl (Index) einen String (Name) als Schlüssel verwenden kannst. So ein Name heißt Schlüssel.
Wenn du schnell einen Wert per Schlüssel finden willst (z.B. per Ausweisnummer den Namen eines Lesers), brauchst du ein Dictionary.
using System.Collections.Generic;
var telefonbuch = new Dictionary<string, string>();
telefonbuch["Anja"] = "+79992221133";
telefonbuch["Maxim"] = "+79998887766";
Console.WriteLine(telefonbuch["Anja"]); // +79992221133
// Du kannst prüfen, ob ein Schlüssel existiert:
if (telefonbuch.ContainsKey("Rutger"))
{
Console.WriteLine(telefonbuch["Rutger"]);
}
else
{
Console.WriteLine("Keine solche Nummer!");
}
Fun Fact: Dictionaries werden oft "assoziative Arrays" genannt. Sie basieren auf einer Hash-Tabelle, was extrem schnellen Zugriff per Schlüssel ermöglicht (fast instant, wenn man Kollisionen ignoriert – aber dazu später mehr).
Wichtige Merkmale
- Schlüssel sind einzigartig: Es kann keine zwei gleichen Schlüssel im Dictionary geben.
- Werte dürfen sich wiederholen.
- Sehr schneller Zugriff, Hinzufügen und Entfernen per Schlüssel.
5. Set: HashSet<T>
Neben Listen und Dictionaries sind auch Sets sehr beliebt. Sie sind fast wie Listen, nur einfacher: eine Liste ohne feste Reihenfolge. Ein Set speichert einfach eine Sammlung von Werten, wenn die Reihenfolge egal ist.
Zum Beispiel, wenn du nur wissen willst, ob etwas in der Sammlung ist, ohne Duplikate, und die Reihenfolge egal ist – dann nimm ein Set.
using System.Collections.Generic;
var bekannteUser = new HashSet<string>();
bekannteUser.Add("admin");
bekannteUser.Add("gast");
bekannteUser.Add("admin"); // Doppelt – wird ignoriert
Console.WriteLine(bekannteUser.Contains("admin")); // True
Console.WriteLine(bekannteUser.Count); // 2
Ein Set prüft extrem schnell, ob ein Element vorhanden ist.
Wozu Sets?
— Wenn du z.B. alle einzigartigen User speichern willst, die die App im Monat geöffnet haben, oder eine Liste einzigartiger Buchautoren.
Merkmale
- Speichert nur einzigartige Elemente (Duplikate werden ignoriert).
- Keine Indizes wie bei Listen.
- Schnelle Prüfung auf Vorhandensein.
6. Queues und Stacks
Es gibt noch Strukturen für Spezialfälle: Queue und Stack. Im Grunde sind das auch Listen, aber mit kontrolliertem Hinzufügen und Entfernen.
Queue<T>: Queue (first in – first out)
Manchmal brauchst du eine "Warteschlange" – neue Elemente kommen ans Ende, entnommen wird immer vom Anfang.
using System.Collections.Generic;
var queue = new Queue<string>();
queue.Enqueue("Erster");
queue.Enqueue("Zweiter");
queue.Enqueue("Dritter");
Console.WriteLine(queue.Dequeue()); // "Erster"
Console.WriteLine(queue.Peek()); // "Zweiter", aber bleibt drin
Stack<T>: Stack (first in – last out)
Ein Stack funktioniert umgekehrt: Der Letzte rein – der Erste raus. Wird z.B. in Parsern, Funktionsaufrufen und Undo-Funktionen in Editoren verwendet.
using System.Collections.Generic;
var stapel = new Stack<string>();
stapel.Push("Eins");
stapel.Push("Zwei");
stapel.Push("Drei");
Console.WriteLine(stapel.Pop()); // "Drei"
Console.WriteLine(stapel.Peek()); // "Zwei"
7. Tabelle: Vergleich der wichtigsten Collections
| Collection | Einzigartige Elemente | Zugriff per Index | Schneller Zugriff per Schlüssel | Einfüge-/Entfern-Operationen |
|---|---|---|---|---|
|
Nein | Ja | Nein | Schnell am Ende |
|
Schlüssel | Nein | Ja | Schnell per Schlüssel |
|
Ja | Nein | Ja* | Schnell per Wert |
|
Nein | Nein | Nein | Schnell (FIFO) |
|
Nein | Nein | Nein | Schnell (LIFO) |
Jede Collection schauen wir uns in den nächsten Vorlesungen noch genauer an. Und wir starten mit dem mysteriösen <T>...
GO TO FULL VERSION