4.2 Die erweiterte for-Schleife
for-Schleifen laufen oft Arrays oder Datenstrukturen ab. Bei der Berechnung des Mittelwerts konnten wir das ablesen:
double sum = 0;
for ( int i = 0; i < array.length; i++ )
sum += array[ i ];
double arg = sum / array.length;
Die Schleifenvariable i hat lediglich als Index ihre Berechtigung; nur damit lässt sich das Element an einer bestimmten Stelle im Array ansprechen.
Weil das komplette Durchlaufen von Arrays häufig ist, gibt es eine Abkürzung für solche Iterationen:
for ( Typ Bezeichner : Array )
...
Die erweiterte Form der for-Schleife löst sich vom Index und erfragt jedes Element des Arrays. Das können Sie sich als Durchlauf einer Menge vorstellen, denn der Doppelpunkt liest sich als »in«. Rechts vom Doppelpunkt steht immer ein Array oder, wie wir später sehen werden, etwas vom Typ Iterable, wie eine Datenstruktur. Links wird eine neue lokale Variable deklariert, die später beim Ablauf jedes Element der Sammlung annehmen wird.
Die Berechnung des Durchschnitts lässt sich nun umschreiben. Die statische Methode avg(…) soll mit dem erweiterten for über die Schleife laufen, anstatt den Index selbst hochzuzählen. Eine Ausnahme zeigt an, ob der Array-Verweis null ist oder das Array keine Elemente enthält:
static double avg( double[] array ) {
if ( array == null || array.length == 0 )
throw new IllegalArgumentException( "Array null oder leer" );
double sum = 0;
for ( double n : array )
sum += n;
return sum / array.length;
}
Zu lesen ist die for-Zeile demzufolge als »Für jedes Element n vom Typ double in array tue …«. Eine Variable für den Schleifenindex ist nicht mehr nötig.
Anonyme Arrays in der erweiterten for-Schleife nutzen
Rechts vom Doppelpunkt lässt sich auf die Schnelle ein Array aufbauen, über welches das erweiterte for dann laufen kann:
for ( int prime : new int[]{ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31 } )
System.out.println( prime );
Das ist praktisch, um über eine feste Menge von Werten zu laufen. Das funktioniert auch für Objekte, etwa Strings:
for ( String name : new String[]{ "Cherry", "Gracel", "Fe" } )
System.out.println( name );
[»] Hinweis
Rechts vom Doppelpunkt kann ein Array oder ein Objekt vom Typ Iterable stehen:
for ( String name : Arrays.asList( "Cherry ", "Gracel", "Fe" ) )
System.out.println( name );
Arrays.asList(…) erzeugt kein Array als Rückgabe, sondern baut aus der variablen Argumentliste eine Sammlung auf, die von einem speziellen Typ Iterable ist; alles, was Iterable ist, kann die erweiterte for-Schleife ablaufen. Wir kommen später noch einmal darauf zu sprechen. Unabhängig vom erweiterten for hat die Nutzung von Arrays.asList(…) noch einen anderen Vorteil, etwa bei Ist-Element-von-Anfragen, etwa so:
if ( Arrays.asList( 1, 2, 6, 7, 8, 10 ).contains( number ) )
…
Mehr zu der Methode folgt in Abschnitt 4.6, »Die Klasse Arrays zum Vergleichen, Füllen, Suchen und Sortieren nutzen«.
Umsetzung und Einschränkung
Intern setzt der Compiler diese erweiterte for-Schleife ganz klassisch um, sodass der Bytecode unter beiden Varianten gleich ist. Nachteile dieser Variante sind jedoch:
Das erweiterte for läuft standardmäßig immer das ganze Array ab. Ein Anfang- und ein Ende-Index können nicht ausdrücklich gesetzt werden.
Die Ordnung ist immer »von vorn nach hinten«.
Die Schrittlänge ist immer 1.
Der Index ist nicht sichtbar.
Die Schleife liefert ein Element, kann aber nicht in das Array schreiben.
Abbrechen lässt sich die Schleife mit einem break. Bestehen andere Anforderungen, kann weiterhin nur eine klassische for-Schleife helfen.
Beispiel: Arrays mit Strings durchsuchen
In unserem ersten Beispiel soll ein nichtprimitives Array Strings referenzieren und später schauen, ob eine Benutzereingabe im Array ist. String-Vergleiche lassen sich mit equals(…) realisieren:
String[] validInputs = { "Banane", "Apfel", "Kirsche" };
boolean found = false;
while ( ! found ) {
String input = new Scanner( System.in ).nextLine();
for ( String s : validInputs )
if ( s.equals( input ) ) {
found = true;
break;
}
}
System.out.println( "Gültiges Früchtchen eingegeben" );
Zur Initialisierung des Arrays nutzt das Programm eine kompakte Variante, die drei Dinge vereint: den Aufbau eines Array-Objekts (mit Platz für drei Referenzen), die Initialisierung des Array-Objekts mit den drei Objektreferenzen und schlussendlich die Initialisierung der Variablen validInputs mit dem neuen Array – alles in einer Anweisung.
Für die Suche kommt das erweiterte for zum Einsatz, das in einer Schleife eingebettet ist, die genau dann endet, wenn das Flag found gleich true wird. Wenn wir nie einen korrekten String eingeben, wird die äußere Schleife auch nie enden. Wenn wir einen Eintrag finden, kann das flag gesetzt werden und break die Array-Schleife früher verlassen.
Zufällige Spielerpositionen erzeugen
Im zweiten Beispiel sollen fünf zufällig initialisierte Punkte in einem Array abgelegt werden. Die Punkte sollen Spieler repräsentieren.
Zunächst benötigen wir ein Array:
Point[] players = new Point[ 5 ];
Die Deklaration schafft Platz für fünf Verweise auf Punkt-Objekte, aber kein einziges Point-Objekt ist angelegt. Standardmäßig werden die Array-Elemente mit der null-Referenz initialisiert, sodass System.out.println(players[0]) die Ausgabe »null« auf den Bildschirm bringen würde. Bei null wollen wir es nicht belassen, daher müssen die einzelnen Array-Plätze etwa mit players[0] = new Point() initialisiert werden.
Zufallszahlen erzeugt die mathematische Methode Math.random(). Da die statische Methode jedoch Fließkommazahlen zwischen 0 (inklusiv) und 1 (exklusiv) liefert, werden die Zahlen zunächst durch Multiplikation frisiert und dann abgeschnitten.
Im letzten Schritt geben wir ein Raster auf dem Bildschirm aus, in dem zwei ineinander verschachtelte Schleifen alle x/y-Koordinaten des gewählten Bereichs ablaufen und dann ein & setzen, wenn der Punkt einen Spieler trifft.
Das Programm als Ganzes:
Point[] players = new Point[ 5 ];
for ( int i = 0; i < players.length; i++ )
players[ i ] = new Point( (int)(Math.random() * 40),
(int)(Math.random() * 10) );
for ( int y = 0; y < 10; y++ ) {
for ( int x = 0; x < 40; x++ )
if ( Arrays.asList( players ).contains( new Point(x,y) ) )
System.out.print( "&" );
else
System.out.print( "." );
System.out.println();
}
Der Ausdruck Arrays.asList(players).contains(new Point(x, y)) testet, ob irgendein Punkt im Array players gleich dem Punkt mit den x/y-Koordinaten ist.
Die Ausgabe erzeugt zum Beispiel Folgendes:
........................................
...............&........................
&.......................................
........................................
........................................
..............................&.........
........................................
...&....................&...............
........................................
........................................
Während die erweiterte for-Schleife gut das Array ablaufen kann, funktioniert das zur Initialisierung nicht, denn das erweiterte for ist nur zum Lesen gut. Elementinitialisierungen funktionieren bei Arrays nur mit players[i]=…, und dazu ist eben eine klassische for-Schleife mit dem Index nötig.