1. Einführung
Stell dir vor, du führst eine Einkaufsliste. Heute musst du 3 Sachen kaufen, morgen 10 und übermorgen nur eine. Wenn du ein Array benutzen würdest, müsstest du jedes Mal ein neues Array für die genaue Anzahl der Sachen anlegen. Oder du machst ein riesiges Array "für alle Fälle" und "schleppst" dann jede Menge leere Plätze mit dir rum. Klingt nicht gerade effizient, oder?
Und jetzt stell dir vor, du hast einen magischen Rucksack, der sich von selbst vergrößert, wenn du Sachen reinpackst, und kleiner wird, wenn du sie rausnimmst. Du musst vorher nicht wissen, wie viele Sachen du reinlegst. Du packst einfach rein, und der Rucksack passt sich an. Genau das ist List<T> in der Programmierwelt von C#!
List<T> ist eine generische Collection, die eine Sequenz von Elementen eines bestimmten Typs speichern kann. Das Schlüsselwort hier ist "dynamisch". Im Gegensatz zu Arrays kann List<T> ihre Größe zur Laufzeit ändern. Sie verwaltet ihre interne Kapazität selbst und vergrößert sie bei Bedarf.
Was bedeutet der Buchstabe T in List<T>? Das ist ein sogenannter Typ-Parameter. Wenn du eine List erstellst, gibst du an, welchen Datentyp sie anstelle von T speichern soll. Zum Beispiel speichert List<int> nur ganze Zahlen, List<string> nur Strings und List<double> nur Gleitkommazahlen. Das garantiert, dass du nicht aus Versehen einen Apfel in eine Orangenliste packst – ziemlich wichtig für die Stabilität und Vorhersagbarkeit deines Codes.
Wozu braucht man das?
In echten Projekten weißt du oft vorher nicht, wie viele Elemente du haben wirst. Zum Beispiel:
- Liste der User, die sich auf der Website eingeloggt haben.
- Liste der Produkte im Warenkorb eines Online-Shops.
- Liste der Suchergebnisse für eine Anfrage.
- Liste der Aufgaben im Taskplaner, den wir jetzt gleich bauen werden.
In all diesen Fällen kann sich die Anzahl der Elemente ändern, und List<T> wird zu unserem unverzichtbaren Helfer.
Wichtige Methoden von List<T>
| Methode | Beschreibung | Beispiel |
|---|---|---|
|
Fügt ein Element ans Ende der Liste an | |
|
Fügt ein Element an einer bestimmten Position ein | |
|
Entfernt das erste Vorkommen eines Elements | |
|
Entfernt das Element am angegebenen Index | |
|
Leert die Liste komplett | |
|
Prüft, ob das Element in der Liste ist | |
|
Index des ersten Vorkommens | |
|
Anzahl der Elemente in der Liste | |
|
Kapazität des internen Puffers | |
2. Erstellen einer Liste (List<T>)
Los geht's mit der Praxis! Stell dir vor, wir wollen einen einfachen Aufgabenmanager bauen. Wir brauchen eine Liste, in die wir unsere Tasks eintragen.
Um eine neue List<T> zu erstellen, benutzen wir den Operator new, genau wie bei anderen Objekten.
Wie deklariert und erstellt man eine List
Die Syntax zum Erstellen einer Liste sieht fast aus wie bei Arrays, nur dass du spitze Klammern statt eckigen benutzt:
using System.Collections.Generic;
List<int> numbers = new List<int>();
Kommentare für alle, die es genau wissen wollen:
- List<int> ist eine Liste von ganzen Zahlen (int). Statt int kannst du jeden anderen Typ nehmen: string, double, eigene Klassen usw.
- Nach dem Erstellen ist die Liste leer – ihre Länge ist 0.
Beispiel: Erster Schritt für unsere App
Angenommen, wir haben eine App, in der der User seinen Namen und sein Alter eingibt (noch aus den ersten Vorlesungen). Jetzt wollen wir die Möglichkeit einbauen, mehrere Namen in einer Liste zu speichern, um später damit zu arbeiten.
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<string> names = new List<string>();
Console.WriteLine("Gib drei Namen ein:");
for (int i = 0; i < 3; i++)
{
string name = Console.ReadLine();
names.Add(name);
}
Console.WriteLine("Du hast eingegeben:");
foreach (string n in names)
{
Console.WriteLine(n);
}
}
}
Achte auf Add(). Genau diese Methode fügt neue Elemente zur Liste hinzu. Sie macht die Liste überhaupt erst dynamisch!
Du kannst eine Liste auch direkt mit Elementen erstellen!
List<string> planets = new List<string> { "Merkur", "Venus", "Erde" };
Super praktisch, wenn du das "Start-Lineup" schon kennst. Diese Syntax wird oft in Tests und bei der Initialisierung von festen Daten verwendet.
3. Grundlegende Operationen mit List<T>
Bevor wir zu den Tricks und zur Magie kommen, schauen wir uns erstmal das "ABC" der Liste an.
Elemente hinzufügen: Add()
Die Methode Add() zu benutzen ist wie eine neue Sache ins Regal zu legen:
List<int> numbers = new List<int>();
numbers.Add(42);
numbers.Add(100);
Jetzt drin: [42, 100]
Anzahl der Elemente bekommen: Count
Im Gegensatz zum Array heißt die Eigenschaft bei Listen nicht Length, sondern Count:
int count = numbers.Count; // Gibt 2 zurück
Merke: Bei Listen benutzt du Count, bei Arrays und Strings Length. Das sind verschiedene Properties für ein ähnliches Ziel.
Zugriff auf Elemente per Index
Zwei Wege, beide machen das gleiche:
int first = numbers[0]; // 42
numbers[1] = 200; // 100 wird zu 200
Wichtig: Die Indizierung startet wie bei Arrays bei null. Versuch nicht, auf numbers[3] zuzugreifen, wenn du nur drei Elemente hast!
Elemente der Liste durchlaufen
Du kannst eine for- oder foreach-Schleife nehmen:
// Mit for – wenn du den Index brauchst
for (int i = 0; i < numbers.Count; i++)
{
Console.WriteLine(numbers[i]);
}
// Mit foreach – wenn du den Index nicht brauchst
foreach (int number in numbers)
{
Console.WriteLine(number);
}
foreach ist meistens einfacher und übersichtlicher, vor allem bei großen Collections.
Aber damit kannst du die Elemente nicht per Index ändern oder mit break vorzeitig aussteigen, außer du baust extra Konstrukte ein.
Element an bestimmtem Index einfügen: Insert()
Die Methode Insert() brauchst du, wenn du ein Element nicht ans Ende, sondern irgendwo dazwischen einfügen willst. Zum Beispiel an zweiter Stelle:
numbers.Insert(1, 55); // Fügt 55 an Index 1 ein
Jetzt numbers: [42, 55, 200]
Elemente entfernen
Aus einer Liste zu löschen ist fast so einfach wie hinzuzufügen:
numbers.Remove(55); // Entfernt das ERSTE Vorkommen von 55
numbers.RemoveAt(0); // Entfernt das Element am Index 0
numbers.Clear(); // Macht die Liste komplett leer
Lösch-Nuancen:
- Wenn du mit Remove einen Wert entfernst, der nicht existiert, passiert nichts Schlimmes: Die Liste bleibt wie sie ist, es gibt keinen Fehler.
- Nach Clear() ist die Liste wieder leer.
4. Nützliche Details
Wie wächst List<T>
Wenn du Elemente zu List<T> hinzufügst, wächst sie nicht jedes Mal ein kleines Stück. Stattdessen reserviert List<T> im Voraus einen Speicherblock (interne Kapazität – Capacity).
Sobald mehr Elemente reinkommen als die aktuelle Kapazität, legt die Liste einen neuen, größeren Speicherblock an (meist doppelt so groß) und kopiert alle alten Werte rüber:
Elemente hinzufügen → passt rein → passt rein → passt rein →
Neues Element passt nicht → größerer Array wird angelegt → Daten werden kopiert → weiter geht's
So kannst du schneller Elemente hinzufügen, weil der Speicher nicht bei jedem Add neu verteilt wird, sondern nur ab und zu.
var list = new List<int>(); // Capacity = 0
list.Add(1); // Capacity = 4
list.Add(2); // Capacity = 4
list.Add(3); // Capacity = 4
list.Add(4); // Capacity = 4
list.Add(5); // Capacity = 8 (vergrößert!)
Capacity vs. Count: Was ist der Unterschied?
- Count – Anzahl der Elemente, die du zur Liste hinzugefügt hast.
- Capacity – Größe des internen Arrays, das die Elemente speichert.
List<int> nums = new List<int>();
Console.WriteLine(nums.Count); // 0
Console.WriteLine(nums.Capacity); // Meist 0 oder ein kleiner Wert
nums.Add(123);
Console.WriteLine(nums.Count); // 1
Console.WriteLine(nums.Capacity); // Jetzt evtl. größer als Count
nums.Capacity = 1000; // Du kannst Capacity explizit erhöhen, wenn du weißt, dass viele Elemente kommen
Die meisten Entwickler benutzen nur Count, aber wenn du Performance-kritische Apps schreibst (z.B. Games oder Datenanalyse), kann das Feintuning von Capacity nützlich sein.
5. Beispiel
Wir bauen unsere kleine App weiter aus – jetzt kann der User zum Beispiel seine Lieblingszahlen eintragen und dann mit der Liste arbeiten: einfügen, löschen, ausgeben und leeren.
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<int> favoriteNumbers = new List<int>();
Console.WriteLine("Gib deine Lieblingszahlen ein (eine pro Zeile, 0 – fertig):");
while (true)
{
int num = int.Parse(Console.ReadLine());
if (num == 0) break;
favoriteNumbers.Add(num);
}
Console.WriteLine("Deine Lieblingszahlen:");
foreach (int n in favoriteNumbers)
{
Console.Write(n + " ");
}
Console.WriteLine("\nWillst du die erste Zahl löschen? (y/n)");
if (Console.ReadLine().ToLower() == "y")
{
favoriteNumbers.RemoveAt(0);
}
Console.WriteLine("Und jetzt deine Liste:");
foreach (int n in favoriteNumbers)
{
Console.Write(n + " ");
}
}
}
Hinweis: Hier benutzen wir int.Parse. In echten Projekten solltest du die Eingabe immer mit int.TryParse prüfen, damit das Programm nicht bei jedem User-Fehler abstürzt.
6. Häufige Fehler und "Fettnäpfchen" für Anfänger
Sehr beliebt: Du schreibst eine Schleife von 0 bis List.Count, aber während der Schleife (z.B. im foreach) änderst du die Liste selbst (fügen oder löschen von Elementen). Mach das nicht! Die Programmier-Elite nennt das "Ändern der Collection während des Durchlaufens". Meistens bekommst du sofort eine InvalidOperationException.
Beispiel für schlechten Code:
foreach (int n in numbers)
{
if (n < 0)
numbers.Remove(n); // BUMM! Laufzeitfehler.
}
Richtig: Entweder erst die Indizes/Elemente zum Löschen in eine Extra-Liste sammeln oder eine for-Schleife rückwärts benutzen.
GO TO FULL VERSION