Die for-each / Enhanced-for (Java)
Wenn du nicht den Index brauchst sondern die Elemente selbst, ist die enhanced-for-Variante deutlich lesbarer:
String[] namen = {"Anna", "Ben", "Cleo"};
// Klassisch mit Index
for (int i = 0; i < namen.length; i++) {
System.out.println(namen[i]);
}
// Enhanced-for (for-each), gleicher Output
for (String name : namen) {
System.out.println(name);
}
Funktioniert für Arrays und alles was Iterable<T> implementiert (List, Set, etc.). Was du nicht machen kannst: das Element via name = "anders" im Array überschreiben, weil name nur eine Kopie der Referenz ist. Brauchst du den Index zur Mutation, klassisches for nutzen.
Vorsicht bei mutable Objekten: bei for (Person p : personen) ist p nur eine Referenz-Kopie. Du kannst das Objekt mutieren (p.setName("...") greift), aber du kannst nicht die Stelle im Array umbiegen (p = new Person(...) macht nichts am Array). Bei immutable Typen wie String oder Integer kannst du das Objekt selbst nicht mutieren. Eine Zuweisung an die for-each-Variable ist zwar syntaktisch möglich, ersetzt aber nicht das Element im Array oder in der Collection.
Die while-Schleife
while prüft die Bedingung vor jedem Durchlauf. Wenn die Bedingung beim ersten Check schon false ist, läuft der Körper null Mal. Im einfachsten Fall lässt sich while als "for-Ersatz mit Handarbeit" lesen, du musst Zähler-Init und Inkrement selbst schreiben:
int i = 0;
while (i < 5) {
System.out.println("Runde " + i);
i++;
}
i = 0
while i < 5:
print(f"Runde {i}")
i += 1
Trace:
Iteration i (vor Check) i < 5 Ausgabe nach i++
─────────────────────────────────────────────────────────
1 0 true "Runde 0" 1
2 1 true "Runde 1" 2
3 2 true "Runde 2" 3
4 3 true "Runde 3" 4
5 4 true "Runde 4" 5
- 5 false - -
Echter Use-Case für while (wo for unpraktisch wäre): Eingabe-Loops. Du weißt vorher nicht wie oft, sondern liest bis Abbruch-Wert:
eingabe = input("Zahl (oder 'q' zum Beenden): ")
while eingabe != "q":
print(f"Du hast {eingabe} eingegeben.")
eingabe = input("Zahl (oder 'q' zum Beenden): ")
Die do-while-Schleife (nur Java)
do { ... } while (cond); prüft die Bedingung nach dem Körper, der Körper läuft also garantiert mindestens einmal. Klausur-Klassiker:
int versuch = 0;
do {
System.out.println("Versuch " + versuch);
versuch++;
} while (versuch < 3);
// Ausgabe: Versuch 0, Versuch 1, Versuch 2
Trace:
Iteration Körper läuft versuch (vor) versuch++ Check (versuch < 3)
──────────────────────────────────────────────────────────────────────────
1 "Versuch 0" 0 1 true
2 "Versuch 1" 1 2 true
3 "Versuch 2" 2 3 false -> Schleife endet
Selbst wenn die Bedingung von Anfang an false ist, läuft der Körper einmal:
int x = 100;
do {
System.out.println("Hallo"); // wird AUSGEGEBEN, obwohl x < 5 false ist
} while (x < 5);
// Ausgabe: Hallo (genau einmal)
Python hat das nicht direkt, man baut das Muster nach:
while True:
eingabe = input("Passwort: ")
if eingabe == "geheim":
break
break und continue im Detail
Beide Anweisungen unterbrechen den normalen Schleifen-Ablauf, aber unterschiedlich:
// break: Schleife komplett verlassen
for (int i = 0; i < 10; i++) {
if (i == 5) break;
System.out.print(i + " ");
}
// Ausgabe: 0 1 2 3 4
// continue: aktuelle Iteration überspringen, weiter mit der nächsten
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) continue;
System.out.print(i + " ");
}
// Ausgabe: 1 3 5 7 9
Trace break-Beispiel:
Iter i (vor) i == 5? Aktion Ausgabe
─────────────────────────────────────────────────
1 0 false print + i++ "0 "
2 1 false print + i++ "1 "
3 2 false print + i++ "2 "
4 3 false print + i++ "3 "
5 4 false print + i++ "4 "
6 5 true break Schleife endet
Trace continue-Beispiel (i=0..5):
Iter i (vor) i%2==0? Aktion Ausgabe
─────────────────────────────────────────────────
1 0 true continue -
2 1 false print + i++ "1 "
3 2 true continue -
4 3 false print + i++ "3 "
5 4 true continue -
6 5 false print + i++ "5 "
Vorsicht in while-Schleifen: nach continue wird die Bedingung wieder geprüft, der Schleifenkörper aber von vorne. Wenn du den Zähler nicht VOR dem continue erhöhst, kann es zur Endlosschleife kommen:
int i = 0;
while (i < 10) {
if (i == 5) continue; // ENDLOSSCHLEIFE, i bleibt bei 5
System.out.println(i);
i++;
}
In dieser klassischen Java-for-Schleife mit i++ im Header passiert das nicht, weil continue zuerst den Update-Ausdruck ausführt. Fehlt der Update-Ausdruck (for (int i = 0; i < n;)), kann auch eine for-Schleife endlos werden.
Schleifeninvariante
Korrektheits-Klausuren fragen oft: "Warum macht diese Schleife was sie soll?" Die Antwort ist eine Schleifeninvariante: eine Aussage, die in jedem Iterationszyklus wahr bleibt.
Wichtige Abgrenzung: Nicht jede Variable ist eine Invariante. Eine Invariante ist eine Aussage über Variablen, die vor jedem Schleifendurchlauf gilt. Sie ist kein Zustand in einem Moment, sondern ein logisches Statement das die ganze Schleife durchhält.
Drei Schritte zum Beweis:
- Initialisierung, Invariante gilt VOR dem ersten Durchlauf.
- Erhaltung, wenn die Invariante vor dem Körper gilt, gilt sie auch nach
i++ wieder.
- Terminierung, wenn die Schleife endet, folgt aus Invariante + endgültiger Bedingung das gewünschte Ergebnis.
Konkret am Maximum-Suchen:
int[] zahlen = {3, 7, 2, 8, 5};
int max = zahlen[0];
for (int i = 1; i < zahlen.length; i++) {
if (zahlen[i] > max) {
max = zahlen[i];
}
}
Invariante: "Vor jedem Schleifenkopf gilt: max ist das Maximum von zahlen[0..i-1]."
| Schritt | Argument |
|---|
| Init | i=1, max=zahlen[0]. zahlen[0..0] ist nur {zahlen[0]}, dessen Max ist zahlen[0] = max ✓ |
| Erhaltung | Vor dem Körper: max = Max(zahlen[0..i-1]). Im Körper wird max ggf. auf zahlen[i] hochgesetzt → nach Körper: max = Max(zahlen[0..i]). Dann i++ → Invariante mit neuem i ist wieder Max(zahlen[0..i-1]) ✓ |
| Term | Schleife endet bei i = zahlen.length. Invariante: max = Max(zahlen[0..length-1]) = Max des gesamten Arrays ✓ |
Die Invariante ist der rote Faden der die Schleife korrekt macht.
Häufige Fehler
- Off-by-one: falsche Wahl von
< vs. <=. Je nach Zielbereich fehlt dadurch eine Iteration oder läuft eine zu viel. <= ist nicht generell falsch (z. B. "Ausgabe 1 bis 10 in Java" braucht i <= 10).
- Unendliche Schleife: Zähler wird in die falsche Richtung verändert oder gar nicht, die Bedingung bleibt immer wahr und das Programm hängt.
- Zähler nicht initialisiert auf einen sinnvollen Wert.
- continue ohne Zähler-Inkrement in while-Schleifen (siehe oben).
- Nested Loops mit gleichem Zählernamen, in Java Compile-Error, in Python stiller Bug:
// Java: Compile-Error im inneren for
for (int i = 0; i < 3; i++) {
for (int i = 0; i < 5; i++) { // ERROR: i is already defined
// ...
}
}
# Python: kein Block-Scope -> inneres for überschreibt i nach der inneren Schleife
for i in range(3):
for i in range(5):
pass
print(i) # Ausgabe: 4, 4, 4 (NICHT 0, 1, 2!), i wurde durch innere Schleife überschrieben
Faustregel: bei verschachtelten Loops klare unterschiedliche Namen (i/j oder row/col) verwenden, dann gibt's diesen Bug nie.