3.2 Schleifen und Felder

Schleifen erlauben die Wiederholungen von Einzelanweisung. Schleifen bestehen typischerweise aus den folgenden Komponenten

  • einen Schleifenrumpf mit den auszuführenden Anweisungen
  • einem Kopf oder Fuß mit einer Ausführungsbedingung
  • oft einer Laufvariablen mit der die Durchläufe kontrolliert werden

Die while Schleife 

Die while Schleife überprüft vor dem Schleifeneintritt ob die Ausführungsbedingung (noch) erfüllt ist. Sie enthält die Schleifenbedingung im Kopf der Schleife.

Die Syntax der while Schleife ist die folgende:
while ( Bedingung) { Anweisung ; } 

oder

while ( Bedingung) Anweisung; 

Beispiel: Arithmetische Summe 1+2+3+4+5

int i = 1;
int summe= 0;
while (i<=5) {
   summe = summe + i;
   i++;
}

UML Diagramm while Schleife

Die do-while-Schleife

Die do-while-Schleife überprüft nach dem ersten Schleifendurchlauf ob die Ausführungsbedingung erfüllt ist. Sie enthält die Schleifenbedingung im Fuß der Schleife. Die Syntax der do-while Schleife ist die folgende:

do { Anweisung ; } while ( Bedingung );

oder

do Anweisung; while ( Bedingung );

Beispiel: Beende Schleife wenn Wert durch 3 ohne Rest teilt

int i = 9;
int j = 0; // Zählt Schleifendurchläufe
do {
   j++;
   i--;
} while (i%3 != 0);

Die Schleife wird dreimal durchlaufen.

UM Diagramm do while Schleife

Regel:

  • Die while Schleife ist eine abweisende Schleife: Sie wird nicht notwendigerweise durchlaufen.
  • Die do-while ist eine nicht abweisende Schleife: Sie wird mindestens einmal durchlaufen.

Diese Unterscheidung ist in verschiedenen Bereichen wichtig

  • Variablen werden bei einer abweisenden Schleife eventuell nicht belegt
  • Es wird manchmal in der Qualitätssicherung gefordert, dass alle Zeilen eines Programmes duchlaufen werden. Bei abweisenden Schleifen muss man unter Umständen bei der Implementierung der Testabdeckung mehr investieren.

Die for-Schleife

Die for-Schleife überprüft die Schleifenbedingung vor dem Eintritt in die Schleife.

Ihre Syntax ist die anspruchsvollste der drei Schleifenarten:

for (Initialiserung; Bedingung; Veränderung) {Anweisung ;}

oder

for (Initialiserung; Bedingung; Veränderung) Anweisung;

Der Kopf der for-Schleife besteht aus den folgenden drei Teilen:

  • Initialisierungsteil: Laufvariable und Startwert werden festgelegt.
  • Bedingungsteil: Wird der Wert der Bedingung unwahr (false) wird die Schleife verlassen oder nicht betreten. Ansonsten wird sie weiter fortgesetzt.
  • Veränderungsteil: Nach jedem Durchlauf der Schleife wird die Laufvariable entsprechen verändert (Typischerweise inkrementiert oder dekrementiert).

Beispiel einer einfachen for-Schleife

Im folgenden Beispiel wird die for-Schleife "b mal" durchlaufen. Durch das Aufaddieren der Variablen a wird eine Multiplikation von positiven Zahlen ausgeführt.

Ist b gleich Null oder negativ wird die Schleife nicht durchlaufen.

einfache for-Schleife

Geschachtelte Schleifen

Oft ist es notwendig Schleifen zu schachteln. Dies kommt oft bei mehrdimensionalen Datenstrukturen vor bei denen jedes Feld bearbeitet werden muss. Bei jedem Durchlauf der äusseren Schleife wird die innere aufgerufen, die ihre eigenen Durchläufe komplett bei jedem Durchlauf der äusseren Schleife durchführt. Anbei ein Beispiel einer naiven Multiplikation die auf Inkrementieren beruht:

class MultiplizierenNaiv {
    public static void main(String[] args) {
        int a = 5;
        int b = 10;
        int result = 0;
        for (int i = 1; i <= a; i++) {
            System.out.println("Äussere Schleife i = " + i);
            for (int j = 1; j <= b; j++) {
                System.out.println("Innere Schleife j = " + j);
                result++;
            }
        }
        System.out.println(a +"*"+b+" = "+result);
    }
}

Geschachtelte Schleifen sind sehr mächtig und sie haben ein viel größeres Potential Rechnerleistung zu binden. Die Aufwände bei zwei geschachtelten Schleifen können quadratisch steigen im Vergleich zum linearen Aufwand einer einfachen Schleife. Generell gilt, dass eine teure Anweisung in einer Schleife n-mal statt einmal durchlaufen werden kann. Es ist daher wichtig auf die folgenden Dinge zu achten:

  • Lassen Sie alle Anweisungen die man vor oder nach der Schleife ausführen kann aus dem Schleifenblock draussen
  • Definieren und Initialisieren Sie Variablen wenn möglich ausserhalb der Schleife und verwenden Sie die Variablen wieder. Die Variable muss sonst jedes mal neu angelegt und wieder gelöscht werden.
  • Verzichten Sie auf unnötige Schleifendurchläufe
  • Seien Sie vorsichtig bei mehrfach geschachtelten Schleifen. Die Anzahl der Durchläufe kann theoretisch sehr schnell, sehr groß werden. Eine dreifach geschachtelte Schleife mit jeweils 1000 Durchläufen wird eine Milliarde mal ausgeführt!

Sprunganweisungen und Schleifenabbrüche mit continue und break Schlüsselwörtern

Java verfügt über zwei Möglichkeiten Schleifen in der Mitte von Ausführungsblöcken zu verlassen. Diese Programmiertechnik sollte man normalerweise vermeiden.

In manchen Fällen ist sie jedoch nützlich. Schleifenabbrüche werden durch die beiden folgenden Schlüsselwörter gesteuert:

  • break: die weitere Abarbeitung im Schleifenblock/rumpf wird abgebrochen. Das Programm verlässt die Schleife und nimmt die Abarbeitung hinter der Schleife wieder auf. die Abbruchkriterien der Schleife werden hier nicht mehr beachtet.
  • continue: die Abarbeitung des aktuellen Schleifendurchlaufs wird beendet. Die Schleife wird jedoch nicht verlassen. Die nächste Iteration wird geplant ausgeführt

Die break Anweisung ist schon vom switch Befehl her bekannt. Mit ihr kann man auch eine if Bedingung beenden.

Beispiel einer continue Anweisung:

... 
for (int i=0; i<= 100; i++) {
   if (i%2 == 0) continue;
   System.out.println("Die Zahl " + i + " ist ungerade");
}

Bei geraden Zahlen wird die Druckanweisung nach der if Anweisung nicht mehr ausgeführt. Die Schleifen werden jedoch alle durchlaufen.

for mit continue Befehl
Beispiel einer break-Anweisung:
...
for (int i=1; i<= 100; i++) {
   if (i== 32) break;
   System.out.println("Die Zahl " + i + " wurde bearbeitet");
}

Die Zahlen von 1 bis 31 werden ausgedruckt. Anschließend wird die Schleife abgebrochen. Die break und continue Anweisungen beziehen sich immer auf die nächst äussere Schleife. Mit Hilfe von Labels (Marken) kann man auch über mehrere geschachtelte Schleifen nach aussen springen.

for mit break Befehl

Verlassen von Blöcken mit Hilfe von Sprungzielen (Label)

Java erlaubt das Benennen von Blöcken mit Hilfe von "Labeln" (Marken). Mit ihnen kann man mit einer break-Anweisung beliebig viele Blöcke auf einmal verlassen.

Das folgende Beispiel zeigt wie man zum Label "Label1" springt um beide Schleifen auf einmal zu verlassen:

class MultiplizierenNaivLabel {
    public static void main(String[] args) {
        int a = 5;
        int b = 10;
        int result = 0;
        Label1: for (int i = 1; i <= a; i++) {
            System.out.println("Äussere Schleife i = " + i);
            for (int j = 1; j <= b; j++) {
                System.out.println("Innere Schleife j = " + j);
                if ((i == 3) && (j == 3)) break Label1;
                result++;
            }
        }
        System.out.println(a +"*"+b+" = "+result);
        System.out.println("Dieses Ergebnis ist falsch...");
    }  
}

Endlosenschleifen

Endlosenschleifen sind Schleifen die nicht terminieren. Ein Programm mit einer Endlosschleife sieht aus wie ein "hängendes" Programm. Es bindet jedoch sehr wahrscheinlich (mindestens) einen physikalischen Prozessor vollständig!

Anbei einige Beispiele mehr oder weniger leicht zu erkennende Endloschleifen:

while ( true) { /* Anweisung(en) */ }
...
for ( ; ; ) { /* Anweisung(en) */ }
...
int i=10;
while (i>5) i++;
...
int i=0;
while ( i!=99) {i++; i++;}

Der Übersetzer erkennt diverse einfache Endlosschleifen und meldet die darauf folgende Codezeile als "unreachable code".

Trivia: Welcher Obsthändler hat die Adresse 1 Infinite Loop, Cupertino, CA 95014?

Apple Hauptquartier

Felder (Arrays), Einführung

Die folgende Kurzüberblick von Feldern ist notwendig um die Übungen zu den Kontrollstrukturen zu lösen. Felder werden später noch einmal aufgegriffen und genauer behandelt.

Felder sind Datenbehälter mit einer oder mehreren Dimensionen um eine Anzahl primitive Datentypen gleichen Typs aufzunehmen.

Man kann auf die einzelnen Feldelemente wahlfrei, also direkt mit Hilfe eines Index zugreifen. Java benutzt rechteckige Klammern [] um auf einzelne Elemente eines Feldes zuzugreifen.

Initialisieren und Deklarieren von Feldern

Felder sind im Gegensatz zu Variablen mit primitiven Datentyp Objekte. Dies bedeutet, dass die die Variable nur aus einer Referenz besteht. Das Feld selbst muss dynamisch angelegt werden. Hierfür gibt es eine Reihe von Gründen.

  • Der Übersetzer kann nicht wissen wie groß ein Feld werden kann
  • Man kann mit einer Feldvariablen dynamisch neue Felder verwalten
  • Felder können sehr groß sein. Es ist oft klüger das Feld erst anzulegen wenn man es benötigt und die Größe kennt.

Die Deklaration von Feldern geschieht wie folgt:

int[] x;  // Anlegen einer Referenz, es ist noch keine Größe vorgeben, 
                    // es können noch keine Feldelemente benutzt werden<
double[] y; // Anlegen einer Referenz, es ist noch keine Größe vorgeben, 
                    //es können noch keine Feldelemente benutzt werden

Um das eigentliche Feld anzulegen muss der new Operator verwendet werden wie für reguläre Objekte auch um den Speicher zu allozieren. Dies geschieht wie folgt:

int [] x;
x = new int[4];
double[] y;
y = new double[200];

Jetzt zeigt x auf 4 Datenbehälter für Ganzzahlen. Der Zugriff auf die vier Elemente erfolgt über einen Index im Bereich von 0 (erster Wert) bis 3.

Zugriff auf Felder

Das Feld kann nun wie folgt belegt und bearbeitet werden:

int [] x = new int[4];

x[0] = 99;
x[1] = 88;
x[2] = x[0]+x[1];
x[3] = x[0]*x[1];

Bestimmen der Länge eines Feldes

...geschieht mit dem Attribut .length. Hier ein Beispiel:

int[] x;
int groesse;
...
groesse=x.length;

Erweiterte for Schleife (enhanced for-loop)

Seit Java 5 ist es möglich mit der for Schleife Felder komfortabler abzuarbeiten. Man kann mit einer Laufvariable alle Elemente des gegeben Feldes abarbeiten. Wie zum Beispiel:

int [] xxx = { 11, 22, 33, 44, 55};
for ( int position : xxx)  // für Element auf Position position in xxx[]
  System.out.println("Wert: " + position);

Wird das folgende Ergebnis liefern:

Wert: 11 
Wert: 22
Wert: 33
Wert: 44
Wert: 55

 Die Oracle Java Dokumentation erklärt die Einsatzmöglichkeiten und Grenzen der "enhanced for-loop".

Anonymous (not verified)

Mon, 11/24/2014 - 19:29

"int i = 10
while (i > 5 ) i++;"

fehlen nach der Bedingung nicht die { }?
Also {i++;} ?

Das  wurde hier in Syntax nicht vollständig korrekt dargestellt.

Die allgemeine Regel für Programmierblöcke ist: Man darf anstatt mehrerer Befehle in einem Block mit geschweiften Klammern immer auch einen einzelnen Befehl ohne geschweifte Klammer schreiben.

Die Syntax müßte wohl als regulärer Ausdruck so aussehen:

while (bedingung) [{ anweisung; {anweisung;}} , anweisung; ]

Anonymous (not verified)

Mon, 11/09/2015 - 19:18

Im Beispiel einer einfachen for Schleife steht, dass sie b mal durchlaufen wird, jedoch ist die Bedingung der for Schleife i

Stefan Schneider

Thu, 11/12/2015 - 18:26

In reply to by Anonymous (not verified)

Gute Überlegung.
Die Variable i dient zum Schleifenabruch und wird bei jedem Durchlauf imkrementiert. Sie startet bei 1 und endet bei b. Das sollte schon hinkommen.

Anonymous (not verified)

Fri, 11/15/2019 - 15:35

Ich glaube es fehlt bei der while, do-while und for Schleife immer ein Semikolon hinter Anweisung(en).
while (Bedingung) {Anweisung(en) ; }
do {Anweisung(en) ; } while (Bedingung);
for (Initialisierung; Bedingung; Veränderung) {Anweisung(en) ; }

Stefan Schneider

Mon, 11/18/2019 - 16:56

In reply to by Anonymous (not verified)

habe ich verbessert.