Gefällt dir dieser Artikel?

Java: Mehrdimensionale Arrays kopieren

erschienen in der Kategorie Software, am 19.01.2013
Schnatterente
Gestern kam mal wieder eine ganz interessante Computerfrage rein. Blogleser Hans kämpft mit einem Problem in Sachen Java Programmierung. Er hat eine Anwendung geschrieben und steht nun vor dem Problem, ein mehrdimensionales Array, also ein Array von Arrays, kopieren zu müssen. Und leider funktioniert das nicht wie gewollt.

Hans will Folgendes machen: Er will ein Array von Arrays Object[][] kopieren, um nachfolgend in der Kopie Änderungen vorzunehmen, die keine Auswirkungen auf das Original-Array haben sollen. Leider funktioniert das Ganze nicht wie gewünscht (trotz Verwendung der clone() Funktion).

Um zu verstehen, wo der Fehler liegt, muss man wissen, wie Java (im Speicher) mit Objekten umgeht. Hat man eine Variable eines sehr einfachen Datentyps, z.B. einen String oder einen Integer, und kopiert die Variable in eine neue, so legt Java im Speicher tatsächlich ein neues Objekt an und kopiert den Wert des anderen.

Soweit, so gut. Dieses Verhalten legt Java aber nicht an den Tag, wenn das zu kopierende Objekt eine Instanz einer komplexeren Klasse ist. Kopiert man ein solches in eine andere Variable, legt Java im Speicher kein neues Objekt an, sondern speichert nur einen Verweis zum Originalobjekt. Somit wirken sich Änderungen am Objekt auf den Inhalt beider Variablen aus, denn im Speicher verweisen diese auf ein und dieselbe Sache.
// Kopieren einfacher Objekte
int i = 3;
int j = i;
i = 5
System.out.println( i ); // gibt 5 aus
System.out.println( j ); // gibt 3 aus


// Referenzieren komplexer Objekte
// Die Klasse Point enthält nur zwei int-Variablen x und y,
// die einen Punkt im Koordinatensystem beschreiben sollen

Point p = new Point(3, 3);
Point q = p;
p.setLocation(5, 5);
System.out.println( p.getX()+", "+p.getY() ); // gibt 5, 5 aus
System.out.println( q.getX()+", "+q.getY() ); // gibt ebenfalls 5, 5 aus!


Um dieses, für Neulinge vielleicht etwas verwirrende, Verhalten zu korrigieren, gibt es die clone() Methode. Diese ermöglicht es, eine echte Kopie eines Objekts zu erzeugen.
Point p = new Point(3, 3);
Point q = p.clone();
p.setLocation(5, 5);
System.out.println( p.getX()+", "+p.getY() ); // gibt 5, 5 aus
System.out.println( q.getX()+", "+q.getY() ); // gibt 3, 3 aus!


Will man also keine Referenz auf ein Objekt erzeugen, sondern dieses wirklich kopieren, sollte man es Klonen. Dies wusste auch Hans, doch das Klonen seines Arrays Object[][] funktionierte nicht. Der Grund hierfür erklärt sich aber in dem bereits Gesagten. Ein Array ist eine Ansammlung von Objekten eines bestimmten Datentyps. All diese Objekte werden irgendwo im Speicher abgelegt und das Array setzt sich zusammen, aus den Verweisen auf diese Speicheradressen. Auch für Arrays gibt es eine clone() Funktion, die wunderbar funktioniert, wenn man ein Array von einfachen Datentypen klonen will. Ein Array, das Arrays beinhaltet, ist aber ein Objekt, das sich nur aus Verweisen auf andere Arrays zusammensetzt. Folglich ist auch klar, was beim Klonen von mehrdimensionalen Arrays passiert. Java kopiert das Objekt, doch natürlich enthält die Kopie die gleichen Inhalte wie das Original (die Verweise auf die gleichen Speicheradressen). Greift man auf ein Objekt innerhalb des kopierten Arrays zu und verändert dieses, betrifft die Änderung somit auch das Original-Array.

Ein Array von Arrays richtig kopieren

Um diesem Problem Herr zu werden, muss man sein Vorgehen so abändern, dass man die clone() Funktion nur noch auf das innerste Array anwendet. Dazu erstellt man das äußere Array in einer Schleife und klont die inneren Arrays.
Object[][] original; // schon vorhanden und initialisiert
Object[][] clone;

clone = new Object[original.length][];
for (int i=0; i<original.length; i++){
clone[i] = original[i].clone();
}

So einfach ist des Rätsels Lösung. Natürlich wächst der Aufwand des Kopierens richtig schön an, wenn man es mit noch mehr Dimensionen zu tun hat (glücklicherweise ist dies aber eher selten der Fall). Für das Klonen eines dreidimensionalen Arrays Object[][][] wäre schon eine verschachtelte Schleife nötig. Mit jeder weiteren Ebene kommt eine for-Schleife hinzu.
Object[][][] original; // schon vorhanden und initialisiert
Object[][][] clone;

for (int i=0; i<original.length; i++){
Object[][] innerClone = new Object[ original[i].length ][];

for (int j=0; j<original[i].length; j++){
innerClone[j] = original[i][j].clone();
}

clone[i] = innerClone;
}


So, ich hoffe das war jetzt alles halbwegs verständlich erklärt, falls nicht einfach fragen.

Geschnatter

3 Kommentare, selbst mitschnattern << < Seite 1/1 > >>
Anonym, am 26.02.2015 um 20:31 Uhr
Ich habe gesucht und gesucht, um zu verstehen, wie ich eine Kopie eines mehrdimensionalen Arrays hinbekomme und habe im Internet kaum einen Beitrag finden können. Dann auf diesen Beitrag gestoßen und man versteht direkt beim ersten Lesen, worauf man achten muss. Man weiß sofort wo bei einem selbst der Fehler lag. Ein sehr hilfreicher und durch die Beispiele echt guter sowie hilfreicher Beitrag.
Anonym, am 26.07.2016 um 18:57 Uhr
Hallo, mir ging es genauso. Danke für Deine übersichtliche Lösung!
Allerdings ist mir ein kleiner Fehler aufgefallen:
Meines Wissens nach greift man auf die Länge eines Arrays mit Array.length und nicht Array.length() zu, da die Länge eine Eigenschaft des Objekts ist, die einmal bei Initialisierung festgelegt wird.
Anonym, am 26.07.2016 um 19:07 Uhr
O, und das ist mir beim zweiten Lesen noch aufgefallen: Welche Methode rufst du mit innerClone() auf? Soweit ich das verstehe, müsste an dieser Stelle die Zuweisung:
clone[i] = innerClone;
stehen.
Antwort: Danke für deine Hinweise! Du hast mit allem recht, ich habe es oben geändert. Wie sich die ganzen zusätzlichen Klammern eingeschlichen haben, weiß ich nicht. Aber jetzt sollte es stimmen. :)