Arrays

Grundlagen

Hier zunächst kurz die wichtigsten Fakten, die man über Arrays in Java wissen sollte:

  1. Array-Indexe zählen ab 0.
  2. Arrays sind Objekte. Ihre Elemente verhalten sich in vielerlei Hinsicht wie die Attribute anderer Objekte.
  3. Die Elemente eines Arrays, dessen Basistyp ein Referenz-Typ ist, sind Referenzen, nicht Objekte.
  4. Mehrdimensionale Arrays in Java sind Arrays, deren Elemente Referenzen auf weitere Arrays sind.

Ich bekomme eine ArrayIndexOutOfBoundsException. Was hat es damit auf sich?

Eine AIOOBE tritt auf, wenn man versucht, über einen ungültigen Index auf ein Element eines Arrays zuzugreifen. In der mit der Exception gelieferten Meldung steht, welchen Wert der Index bei dem problematischen Zugriffsversuch hatte. Da Java-Arrays mit dem Index 0 beginnen ist der größte mögliche Index um eins kleiner als die Länge des Arrays.

Typische Ursache für eine AIOOBE sind falsche Obergrenzen bei For-Schleifen. Eine korrekte For-Schleife über alle Elemente eines Arrays sieht so aus (man beachte das Kleiner-Zeichen in der Schleifenbedingung, im Gegensatz zu dem von Pascal – wo Arrays ab 1 zählen – gewohnten Kleiner-Gleich):

    for (int i = 0; i < myArray.length; i++) {
        System.out.println(myArray[i]);
    }

Der eleganteste Weg, eine AIOOBE zu vermeiden, besteht darin, gar keinen expliziten Index zu verwenden, sondern die sog. ForEach-Form der For-Schleife zu benutzen (was leider nicht immer möglich ist):

    for (Person person : myArray) {
        System.out.println(person);
    }

Näheres dazu im Abschnitt zum Thema ForEach-Form der For-Schleife

Ich bekomme beim Zugriff auf ein Array-Element eine NullPointerException. Warum?

Die typische Ursache ist, dass Sie zwar den Array selbst erzeugt haben, nicht jedoch seine Elemente. Eine Codezeile wie

    Vogel[] alleVoegel = new Vogel[42];

erzeugt eben nur den Array, nicht jedoch irgendwelche vordefinierten Vogel-Exemplare. Das mag auf den ersten Blick überraschen, wird aber völlig logisch, wenn man sich klarmacht, dass der Compiler kaum dem Programmierer die Entscheidung abnehmen kann, welche Objekte er eigentlich in den Array packen möchte und wie diese zu initialisieren sind. Im Falle der Vögel ist es z. B. recht wahrscheinlich, dass diese Klasse abstrakt ist, es also überhaupt keine direkten Vogel-Exemplare geben kann.

Übrigens verhalten sich Arrays hier völlig analog zu anderen Objekten: Alle Attribute, die nicht explizit initialisiert werden erhalten bei der Erzeugung des Objekts zunächst den Default-Wert des entsprechenden Typs und bei Referenztypen ist dies nun einmal die leere Referenz null.

Ich verwende eine Kopie eines Arrays, aber wenn ich darin Änderungen vornehme, wirken sich diese auf das Original aus. Warum?

Verwenden Sie vielleicht gar keine Kopie? Evtl. haben Sie die vermeintliche Kopie etwa so erzeugt:

    Vogel[] kopie = alleVoegel;

Damit erzeugen Sie aber keinen neuen Array, sondern Sie deklarieren lediglich eine neue Variable „kopie“ , welche denselben Wert hat wie „alleVoegel“. Der Wert der Variablen ist aber nicht der Array, sondern lediglich eine Referenz auf diesen. Diese haben Sie kopiert, d.h. Sie haben also jetzt zwei Variablen, die dasselbe Objekt referenzieren, sogenannte Aliase.

Aber auch wenn ich den Array explizit kopiere, wirken sich Änderungen auf das Original aus!

Nein. Änderungen am kopierten Array wirken sich nicht auf das Original aus. Wahrscheinlich entspricht Ihr Problem dem folgenden Beispiel:

    Person[] original = new Person[2];
    original[0] = new Person("Donald", "Duck");
    original[1] = new Person("Franz", "Gans");
    Person[] kopie = new Person[2];
    System.arraycopy(original, 0, kopie, 0, 2);
    kopie[1].setVorname("Gustav");
    System.out.println(original[1].getVorname());
    System.out.println(kopie[1].getVorname());

Hier erhalten Sie in der Tat zwei mal die Ausgabe „Gustav“. Die Ursache ist aber nicht, dass sich Änderungen an „kopie“ auf „original“ auswirken, sondern, dass die Elemente beider Arrays Referenzen auf dieselben Objekte enthalten, kopie[1] also dasselbe Objekt referenziert wie original[1]. Um sich den Unterschied klarer zu machen, testen Sie einmal folgenden Code, welcher tatsächlich Änderungen am kopierten Array vornimmt.

    Person[] original = new Person[2];
    original[0] = new Person("Donald", "Duck");
    original[1] = new Person("Franz", "Gans");
    Person[] kopie = new Person[2];
    System.arraycopy(original, 0, kopie, 0, 2);
    kopie[1] = new Person("Gustav", "Gans");
    System.out.println(original[1].getVorname());
    System.out.println(kopie[1].getVorname());

Die Dienstleistungsmethode System.arraycopy() macht also letztlich nichts anderes als das, was Sie auch mit einer for-Schleife machen könnten: Sie kopiert Elemente um. Und da die Elemente eines Arrays, der „Objekte enthält“ nun einmal gar nicht die Objekte sind, sondern nur Referenzen auf diese, werden auch nur Referenzen kopiert... die dann natürlich dieselben Objekte referenzieren wie ihre Originale.

Kann ich mit Hilfe von System.arraycopy() auch mehrdimensionale Arrays kopieren?

Ja, aber damit erreichen Sie nicht das, was Sie vermutlich wollen. Denn ein mehr-dimensionaler Array ist in Java nur ein Array, dessen Elemente Referenzen auf weitere Arrays sind. Und beim Kopieren der „ersten Ebene“ würden nur die Referenzen auf die Arrays der zweiten Ebene kopiert, nicht aber diese selbst, siehe auch die vorigen beiden Fragen dieses Kapitels. Wenn Sie eine „tiefe“ Kopie eines mehrdimensionalen Arrays haben wollen, müssen die die äußeren Ebenen „von Hand“ kopieren, also mit (ggf. geschachtelten) for-Schleifen. Für die innerste Ebene können Sie dann statt einer for-Schleife natürlich System.arraycopy() einsetzen.