02.08 Schleifen

Schleifen steuern eine Wiederholung eines bestimmten Programmsegments, bis eine bestimmte Bedingung erreicht ist. Dabei gibt es in Java mehrere Arten von Schleifen. Zuerst unterscheidet man zwischen kopf- und fußgesteuerten Schleifen. Bei kopfgesteuerten Schleifen wird zuerst die Bedingung geprüft und dann der Code ausgeführt, bei einer fußgesteuerten Schleife wird zuerst der Code ausgeführt und dann die Bedingung geprüft. Eine fußgesteuerte Schleife wird also immer mindestens einmal ausgeführt. Außerdem unterscheidet man noch zwischen Zähl– (for) und Während-Schleifen (while).

Die typische Verwendung einer for-Schleife besteht darin, solange einen Zähler hoch- oder runterzuzählen, bis ein bestimmter Wert erreicht ist. Die Syntax sieht wie folgt aus: Zuerst wird die Schleife mit einem for eingeleitet, anschließend werden die Parameter in abgerundeten Klammern definiert. Dabei wird zuerst eine Variable initialisiert, anschließend die Durchlaufbedingung festgelegt, und zum Schluss eine Aktion, die nach jedem Durchlauf ausgeführt werden soll. Die einzelnen Bereiche werden jeweils durch ein Semikolon getrennt. Anschließend wird wieder ein Block geöffnet, in welchem die zu wiederholende Aktion definiert ist. Um bspw. alle Zahlen von 0 bis 9 auszugeben, können Sie folgenden Code verwenden:

for (int i = 0; i < 10; i++) {
  System.out.println(i);
}

Es ist auch möglich mehrere Variablen und Aktionen, die bei jedem Durchgang ausgeführt werden sollen, zu definieren. Außerdem kann die Bedingung in der Mitte wie eine gewöhnliche IF-Bedingung, also mit Und-, Oder- und XOR-Verknüpfungen, aufgebaut werden.

package sprachsyntax.de.main;
public class SchleifenTest1 {
  public static void main(String[] args) {

    System.out.println("Wer ist schneller bei 75?");
    for (int i = 0, j = 40; i < 75 || j < 75; i += 2, j++) {
      System.out.println("I ist jetzt bei: " + i);
      System.out.println("J ist jetzt bei: " + j);
    }
  }
}

Die Bereiche müssen nicht zwingend ausgefüllt werden. So kann die Initialisierung der Variablen durchaus auch vor der Schleife stattfinden. Dies ermöglicht einen Zugriff auf die Variablen nachdem die Schleife bereits abgeschlossen wurde. Denn ansonsten existieren die Variablen freilich nur im Gültigkeitsbereich der Schleife.

package sprachsyntax.de.main;
public class SchleifenTest1 {
  public static void main(String[] args) {

    int i = 0;
    int j = 40;
    System.out.println("Wer ist schneller bei 75?");
    for (; i < 75 || j < 75; i += 2, j++) {
      System.out.println("I ist jetzt bei: " + i);
      System.out.println("J ist jetzt bei: " + j);
    }
    if (i < j) {
      System.out.println("J war zuerst da");
    }
    else if (j < i) {
      System.out.println("I war zuerst da");
    }
    else {
      System.out.println("Beide waren gleichzeitig da");
    }
  }
}

Da jetzt die Ausgaben während der Schleife nicht mehr benötigt werden, können Sie diese wegkürzen. Dies ist nützlich, wenn einfach nur solange eine Aktion durchgeführt werden soll, bis ein bestimmter Status oder ein bestimmtes Ergebnis erreicht wurde. Hierzu wird einfach kein Gültigkeitsbereich angelegt und dafür die Schleife mit einem Semikolon abgeschlossen:

for (; i < 75 || j < 75; i += 2, j++);

Seit Java 1.5 existiert noch eine neue for-Schleife. Da diese aber explizit mit Arrays und Listen zu tun hat, wird diese Art der Schleife erst im nachfolgenden Kapitel erläutert.

Eine andere Schleife ist die while-Schleife. Diese führt Code aus, solange ein bestimmter Zustand besteht. Praktisch gesehen ist eine while-Schleife eine for-Schleife – Nur ohne Initialisierungs- und Veränderungsbereich. Dadurch ist der Aufbau deutlich einfacher.

while (Bedingung) {
  // Mach was
}

Diese Schleife wird zum Beispiel beim Auslesen von Dateien häufig verwendet. Eine Variante, um diese Schleife fuß- anstelle von kopfgesteuert zu verwenden, ist die do-whileSchleife. Dabei wird die Bedingung ans Ende gestellt und selbige auch erst nach Beenden des Codes im Block der Schleife geprüft. Dadurch wird gewährleistet, dass die Schleife immer mindestens einmal durchlaufen wird.

do {
  // Mach was
} while(Bedingung);

Manchmal ist es nötig Schleifen zu verschachteln. In diesem Fall wird eine Schleife im Block einer anderen erstellt. Diese Struktur lässt sich beliebig tief verschachteln. Allerdings ist dabei zu beachten, dass, je tiefer Schleifen verschachtelt sind, desto mehr Rechenleistung und Speicher benötigt wird. Außerdem kann der Code so leicht unübersichtlich werden.

package sprachsyntax.de.main;
public class SchleifenTest2 {
  public static void main(String[] args) {

    for (int i = 0; i < 6; i++) {
      for (int j = i; j > -1; j--){
        System.out.print("*");
      }
      System.out.println();
    }
  }
}

Einen halben „Tannenbaum“ auf die Konsole zu zeichnen, ist natürlich nicht die einzige Verwendungsmöglichkeit von verschachtelten Schleifen. Bei der Abarbeitung dieses Buches und während Ihrer weiteren Programmierarbeit werden Ihnen noch sehr viele (sinnvollere) Beispiele begegnen, in denen Sie verschachtelte Schleifen einsetzen können/müssen.

Eine weitere Verwendungsart ist eine Endlosschleife. Diese läuft, wie der Name schon sagt, endlos lang (bis das Programm abgebrochen wird). Da so das Ausführen der nächsten Programmschritte unterbunden wird, ist eine solche Schleife nur bedingt sinnvoll und wird oft auch nur versehentlich programmiert. Es gibt natürlich auch Fälle, in denen so etwas nützlich ist. Zum Beispiel wenn ständig etwas überprüft werden muss. Diese sollte dann aber in einem extra Thread gestartet werden. Dadurch wird verhindert, dass der Rest des Programms gesperrt wird und somit nicht weiter verwendet werden kann. Möchten Sie eine Endlosschleife absichtlich erzeugen, ist es wichtig, dass in der Bedingung der Schleife immer true steht, bzw. ein dementsprechender boolscher Wert zurückgegeben wird.

for (;;) {
  System.out.println("Ich ende nie!");
}
for (int i = 0; true; i++) {
  System.out.println("I = " + i);
}
while(true) {
  System.out.println("Ich ende ebenfalls nie");
}
do {
  System.out.println("Und ich ende auch nicht");
} while (1 == 1);

Unabsichtliche Endlosschleifen entstehen meistens durch kleine Denk- bzw. Flüchtigkeitsfehler. Zum Beispiel wenn man eine Zählvariable auf 1 dekrementieren möchte, die ganze Zeit aber immer inkrementiert hat. Da kann es schon mal passieren, dass aus dem eigentlich beabsichtigten i-- ein i++ wird.

for (int i = 10; i > 0; i++) {
  System.out.println("Ich bin eine unabsichtliche Endlosschleife");
}

Aber sind Endlosschleifen wirklich so endlos? Nein! Es gibt eine Möglichkeit eine Schleife nicht regulär zu verlassen. Hierbei hilft abermals das Schlüsselwort break.

for (int i = 1; i < 10; i++) {
  int j = 1;
  while (true) {
    j++;
    System.out.println("I: " + i + ", J: " + j);
    if (i % j == 0 || j % i == 0) {
      System.out.println("Treffer!");
      break;
    }
  }
}

Die äußere Schleife läuft 9mal durch. Dabei wird bei jedem Durchlauf die Variable j neu mit dem Wert 1 initialisiert und die Variable i um den Faktor 1 inkrementiert. Anschließend wird eine innere, scheinbar endlose, Schleife gestartet. Diese inkrementiert j jedes Mal um 1, gibt den aktuellen Wert von i und j aus und überprüft, ob eine von den beiden Variablen durch die andere ohne Rest teilbar ist. Trifft dies zu, wird eine Erfolgsmeldung ausgegeben und die innere Schleife abgebrochen. Der Sinn dieser verschachtelten Schleife besteht einzig und alleine darin, Ihnen die Verwendung von break darzustellen.

Eine weitere Verwendungsmöglichkeit von break ist die Kombination mit labels. Mithilfe von labels können Sie exakt definieren, welche Schleifen abgebrochen werden sollen. Ein ebenso sinnloses Beispiel zur Demonstration:

for (int i = 0; i < 10; i++) {
  label:
  for (int j = 0; j < 10; j++) {
    for (int k = 0; k < 10; k++) {
      if (k + j - i > 10) {
        System.out.println(k + " + " + j + " - " + i + " ist größer als 10");
        break label;
      }
    }
  }
}

Durch die Definition des labels und dem Aufruf von break mit dem Namen des labels, können beliebig viele Schleifen verlassen werden. Ohne die Verwendung von labels wird immer nur die aktuelle Schleife verlassen. Um ein label zu setzen, schreiben Sie einen beliebigen Namen gefolgt von einem Doppelpunkt an eine Stelle in einer verschachtelte Schleife.

Zum Abschluss dieses Beitrags lernen Sie noch den Ausdruck continue kennen. Dieser Ausdruck funktioniert ähnlich wie break. Mit dem Unterschied, dass break die komplette Schleife abbricht, continue aber nur den aktuellen Durchlauf.

/* Alle Zahlen ausgeben, die nicht
* durch 3 ohne Rest teilbar sind */
for (int i = 0; i < 20; i++) {
  if (i % 3 == 0) {
    continue;
  }
  System.out.println(i);
}

Übung

4 Replies to “02.08 Schleifen”

  1. Sebastian

    Hallo,

    Um es vorweg zu nehmen, ich bin ein Anfänger und kann total daneben liegen.

    Könnte es sein, dass im dritten Beispiel ein UND && sinnvoller wäre als ein ODER ||?

    Wenn ich die vorherigen Kapitel richtig deute (also wenn(!)), dann zählt die Schleife solange hoch, bis beide Werte „true“ sind. Bei || langt ja ein true um den Schleifendurchlauf zu starten und i landet somit immer bei 76 und j bei 75, ganz egal welche Werte wir ihnen ursprünglich zuweisen.

    Bei && wird die Schleife gestoppt, wenn einer der beiden Variablen die 75 erreicht hat, weil ja dann nicht mehr beide Bedingungen true sind. Danach macht die größer/kleiner/gleich-Prüfung mMn mehr Sinn.

    Hoffe ich konnte erklären, was ich meine bzw. dass ich mich nicht mit der Logik verheddert habe :-).

  2. Stefan Kiesel

    Hallo Sebastian,

    nur weil i oder j zuerst die 75 erreicht, bedeutet das nicht, dass die Variable dann auf einmal nicht mehr hochgezählt wird. Beide Zahlen werden so lange hochgezählt, bis beide größer gleich 75 sind. Natürlich könnte man aber auch mit && verknüpfen. Man käme im Endeffekt auf ein ähnliches Ergebnis, mit dem Unterschied, dass abgebrochen wird, sobald die erste der beiden Variablen die 75 erreicht oder überschreitet.

    Grüße
    Stefan

  3. Tokra

    Wieso ist folgendes Ungültig?
    Ich könnte im for Schleifen Kopf mehrere Variablen des gleichen Typen Deklarieren und Initialisieren,
    sind es aber verschiedene Typen bekomme ich von Compiler einen Error.
    Deklariere ich die verschiedenen Typen von Variablen jedoch außerhalb der for Schleife
    und Initilisiere sie im for Schleifenkopf nur, gibt es kein Problem.

    Vielen Dank

  4. Stefan Kiesel

    Hallo Tokra,

    was erwarten Sie für eine Antwort? Dass im Java-Compiler in der Datei xyz in Zeile 194 spezifiziert ist, dass es nicht funktioniert?

    Generell haben Sie ja schon festgestellt, dass es nicht funktioniert. Das ist so festgelegt und muss von Ihnen akzeptiert werden. Vielleicht überlegen Sie sich einmal den Sinn einer for-Schleife. Eine for-Schleife ist primär dafür da, dass etwas x-mal ausgeführt wird. Alles, was zur „Berechung“ dieser x-mal benötigt wird, sollte in den Kopf der for-Schleife geschrieben werden. In der Regel benötigt man für diese Rechnung eine, in Ausnahmefällen evtl. zwei, Zählvariablen. Mir fällt kein Use-Case ein, bei welchem man zwei unterschiedliche Datentypen zum Rechnen/Zählen in einer for-Schleife benötigen würde, lasse mich aber gerne von Ihnen eines Besseren belehren. Weitere Variablen in den Kopf der for-Schleife zu legen, nur um Codezeilen zu sparen oder marginal weniger Speicher zu benötigen (nachdem die Schleife verlassen wurde), geht stark zu Lasten der Lesbarkeit/Übersichtlichkeit und macht heute einfach keinen Sinn mehr.

    Beste Grüße
    Stefan

Schreibe einen Kommentar