Jetzt halte aber mal die Kopie flach! Von flachen und tiefen Kopien.

Hast du schon mal ein Objekt kopieren wollen und das neue ums Verrecken nicht abändern können, ohne dass sich das Original mit ändert? Dann bist du über eine flache Kopie gestolpert!

flache und tiefe Kopien in Java
Kopieren Studieren! – in Java

Ich bin über eine flache Kopie gestolpert! Über was denn für eine Kopie, äh? Na über die eines komplexen Objekts! … Bist du high?? …

Ein beliebtes Ärgernis – in der Welt der Programmierung – ist das richtige kopieren von Objekten.

So scheint es auf dem ersten Blick unlogisch, dass man eine selbst erstellte Klasse instanziiert, mit Werten bestückt, kopiert (Klasse objektNeu = objektAlt), die neue Instanz abändert und im alten Objekt genau dieselben – abgeänderten – Werte vorfindet.

Macht man dasselbe hingegen bei einem einfachen Integer Datenwert, lässt sich dieser problemlos kopieren, und man erhält anschließend zwei unterschiedliche Werte.

Primitive- und Komplexe-Objekte

Um diesem Phänomen auf die Schliche zu kommen muss man wissen, dass Java – wie auch die meisten anderen Programmiersprachen – zwischen komplexen und primitiven Objekten unterscheidet. Primitive Objekte sind die einfachen Datentypen wie Byte, Short, Integer, Long, Double, String. Diese lassen sich problemlos kopieren und unabhängig abändern.

Auf der anderen Seite stehen die komplexen Objekte, dazu zählen im Prinzip alle anderen Klassen, die wiederrum aus eigenen Methoden und Variablen – welche von primitiver Natur sind – bestehen. Diese lassen sich nicht so einfach kopieren. Hier kommen die flachen und tiefen Kopien ins Spiel.

Flache Kopie

Streng genommen kopiert man bei der flachen Kopie gar nicht. Im eigentlichen setzt man nur einen Verweis. So sagt man dem Programm lediglich, dass das neue Objekt einen Verweis auf das alte bekommt. Dabei sind die beiden Instanzen direkt miteinander verbunden. Ändert man einen Wert in der neuen Instanz, ändert sich dieser auch in der Alten und andersherum.

Flache Kopie: Ein Beispiel

Unser komplexes Objekt, ist eine selbst erstellte Koordinaten-Klasse. Ich habe die Getter und Setter der einfachhalber weggelassen.

/**
 * Beschreibt eine Koordinate in x, y und z
 */
public class Koordinate {
    /** x-Koordinate */
    public int x;
    /** y-Koordinate */
    public int y;
    /** z-Koordinate */
    public int z;

    /** Standardkonstruktor */
    public Koordinate(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    /**
     * gibt die Koordinate in Textform aus
     * @return Kooridnaten als String
     */
    public String getTextual() {
        return "x: " + this.x + "y: " + this.y + "z: " + this.z;
    }
}

Im nächsten Teil erstellen wir zwei Instanzen. Die Original-Koordinate beschreiben wir und geben sie aus. Anschließend wird diese flach in die Kopie-Koordinate kopiert. Jetzt ändern wir die Werte der Kopie-Koordinate und siehe da, in der Ausgabe können wir sehen, dass sich beide Werte geändert haben.

//erstellen von Original
Koordinate originalK = new Koordinate(1,2,3);
//Ausgabe vom Original
System.out.println("1: originalK - " + originalK.getTextual());
//flache kopie erstellen
Koordinate copyK = originalK;
//Ausgabe der flachen Kopie
System.out.println("2: copyK - " + copyK.getTextual());
//neue Werte für Kopie
copyK.x = 11;
copyK.y = 12;
copyK.z = 13;
//Ausgabe Original und Kopie
System.out.println("3: copyK - " + copyK.getTextual());
System.out.println("4: originalK - " + originalK.getTextual());

Ausgabe:

1: originalK - x: 1 y: 2 z: 3
2: copyK - x: 1 y: 2 z: 3
3: copyK - x: 11 y: 12 z: 13
4: originalK - x: 11 y: 12 z: 13

Die Object-ID nach Flacher Kopie

Ganz hilfreich ist dabei die Methode „.toString()“. Diese gibt mir, insofern sie nicht überschrieben wurde, die ID eines Objekts zurück. Dabei sehen wir, dass diese für beide gleich ist.

System.out.println("OriginalID: " + originalK.toString());
System.out.println("KopieID: " + copyK.toString());

Ausgabe:

OriginalID: Koordinate@e53108
KopieID: Koordinate@e53108

Tiefe Kopie

Als tiefe Kopie wird eine echte Kopie bezeichnet. Die Original Koordinate wird kopiert und dabei entsteht ein wirklich neues Objekt. Dieses ist völlig unabhängig vom Original. Um dieses Kopieren jedoch zu ermöglichen, brauchen wir eine neue Methode, vorzugsweise einen Konstruktor der ein komplettes Objekt seiner Selbst entgegennimmt. Dazu erweitern wir unsere Klasse um einen Konstruktor.

Tiefe Kopie: Ein Beispiel

/**
 * Beschreibt eine Koordinate in x, y und z
 */
public class Koordinate {
    /** x-Koordinate */
    public int x;
    /** y-Koordinate */
    public int y;
    /** z-Koordinate */
    public int z;

    /** Standardkonstruktor */
    public Koordinate(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    /** Konstruktor für tiefe Kopie */
    public Koordinate(Koordinate k) {
        this.x = k.x;
        this.y = k.y;
        this.z = k.z;
    }

    /**
     * gibt die Koordinate in Textform aus
     *
     * @return Kooridnaten als String
     */
    public String getTextual() {
        return "x: " + this.x + " y: " + this.y + " z: " + this.z;
    }
}

In diesem Konstruktor sagen wir lediglich, dass wir jeden Wert – der primitiven Datentypen – einzeln Übertragen. Anschließend lassen wir wieder unsere Hauptroutine laufen. Jedoch mit dem Unterschied, dass wir jetzt nicht sagen copy = original, sondern das Original dem Copy-Objekt im Konstruktor übergeben.

//erstellen von Original
Koordinate originalK = new Koordinate(1,2,3);
//Ausgabe vom Original
System.out.println("1: originalK - "+originalK.getTextual());
//flache kopie erstellen
Koordinate copyK = new Koordinate(originalK);
//Ausgabe der flachen Kopie
System.out.println("2: copyK - "+copyK.getTextual());
//neue Werte für Kopie
copyK.x = 11;
copyK.y = 12;
copyK.z = 13;
//Ausgabe Original und Kopie
System.out.println("3: copyK - "+copyK.getTextual());
System.out.println("4: originalK - "+originalK.getTextual());

Ausgabe:

1: originalK - x: 1 y: 2 z: 3
2: copyK - x: 1 y: 2 z: 3
3: copyK - x: 11 y: 12 z: 13
4: originalK - x: 1 y: 2 z: 3

Wir können sehen, dass das Original von der Änderung in der Kopie, völlig unbeeindruckt bleibt. Anschließend machen wir wieder den Test mit der toString()-Methode. Und wir können auch hier sehen, dass nun zwei Unterschiedliche ID’s generiert wurden, somit auch zwei unterschiedliche Objekte existieren.

Die Object-ID nach Tiefer Kopie

System.out.println("OriginalID: " + originalK.toString());
System.out.println("KopieID: " + copyK.toString());

Ausgabe:

OriginalID: Koordinate@f62373
KopieID: Koordinate@19189e1

Anhang

Anbei lege ich noch beide Beispiele zur flachen und tiefen Kopieren

weblink Java – flache Kopie Flache Kopie
weblink Java – tiefe Kopie Tiefe Kopie

MrKnowing

Programmierer und Wissensnerd! Kontaktiere mich auf Google+ oder einfach per Mail danny@mrknowing.com

Das könnte dich auch interessieren …

Eine Antwort

  1. User007 sagt:

    Danke für den tollen Artikel.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert