web-dev-qa-db-de.com

Tiefe Kopie eines Objektarrays

Ich möchte eine tiefe Kopie eines Objektarrays mit einem Konstruktor erstellen.

public class PositionList {
    private Position[] data = new Position[0];

public PositionList(PositionList other, boolean deepCopy) {
        if (deepCopy){
            size=other.getSize();
            data=new Position[other.data.length];
            for (int i=0;i<data.length;i++){
            data[i]=other.data[i];
            }

Was ich jedoch aus irgendeinem Grund oben habe, funktioniert nicht. Ich habe automatisierte Tests, die ich ausführte, und diese Tests schlagen fehl. Also hier ist ein Fehler und ich bin mir nicht sicher, was es ist.

12
Snowman

Was Sie implementiert haben, ist eine flache Kopie. Um eine deep Kopie zu implementieren, müssen Sie diese ändern

data[i] = other.data[i];

zu etwas, das ein Kopie von other.data[i] zu data[i] zuweist. Wie Sie dies tun, hängt von der Klasse Position ab. Mögliche Alternativen sind:

  • ein Kopierkonstruktor:

    data[i] = new Position(other.data[i]);

  • eine Fabrikmethode:

    data[i] = createPosition(other.data[i]);

  • klon:

    data[i] = (Position) other.data[i].clone();

Anmerkungen:

  1. Das oben Gesagte setzt voraus, dass der Kopierkonstruktor, die Factory-Methode und die Klon-Methode die "richtige" Art des Kopierens implementieren, abhängig von der Position-Klasse. siehe unten.
  2. Der clone -Ansatz funktioniert nur, wenn Position ihn ausdrücklich unterstützt, und dies wird im Allgemeinen als minderwertige Lösung angesehen. Außerdem müssen Sie sich bewusst sein, dass die native Implementierung von clone (d. H. Die Methode Object.clone()) eine flache Kopie erstellt.

Tatsächlich ist das allgemeine Problem der Implementierung von Deep Copying in Java kompliziert. Im Fall der Klasse Position würde man annehmen, dass die Attribute alle primitiven Typen sind (z. B. ints oder double), und daher ist ein tiefes versus flaches Kopieren fraglich. Wenn es jedoch Referenzattribute gibt, müssen Sie sich auf den Kopierkonstruktor/die Factory-Methode/die Klon-Methode verlassen, um die Art des Kopierens durchzuführen, die Sie benötigen. In jedem Fall muss es programmiert werden. Und im allgemeinen Fall (wenn Sie sich mit Zyklen befassen müssen) ist es schwierig und erfordert, dass jede Klasse spezielle Methoden implementiert.

Es gibt noch einen anderen möglichen Weg, ein Array von Objekten zu kopieren. Wenn die Objekte im Array serialisierbar sind , können Sie sie mit ObjectOutputStream und ObjectInputStream serialisieren und anschließend deserialisieren Array. Jedoch:

  • das ist teuer,
  • es funktioniert nur, wenn die Objekte (transitiv) serialisierbar sind, und
  • die Werte von transient Feldern werden nicht kopiert.

Kopieren durch Serialisierung wird nicht empfohlen. Es wäre besser, das Klonen oder eine andere Methode zu unterstützen.

Alles in allem wird tiefes Kopieren in Java am besten vermieden.

Zur Beantwortung Ihrer Frage zu den Funktionen des Kopierkonstruktors der Klassen Position erwarte ich, dass dies in etwa so aussieht:

public class Position {
    private int x;
    private int y;
    ...
    public Position(Position other) {
        this.x = other.x;
        this.y = other.y;
    }
    ...
}

Wie @Turtle sagt, ist keine Magie involviert. Sie implementieren einen Konstruktor (von Hand), der seinen Status durch Kopieren von einer vorhandenen Instanz initialisiert.

23
Stephen C

Wenn du sagst:

data[i]=other.data[i];

Sie kopieren lediglich eine Liste von Referenzen (vorausgesetzt, dies ist ein Array von Objekten). Wenn Sie eine tiefe Kopie erstellen möchten, müssen Sie mit new eine neue Instanz jedes Objekts im Array erstellen.

2
Justin Ethier

Anstatt zu sagen:

data[i]=other.data[i]

Sie sollten einen Kopierkonstruktor für Positionerstellen (mit anderen Worten, einen Konstruktor für Position, der einen anderen Positionübernimmt und die primitiven Daten darin kopiert). Sagen Sie data[i]=new Position(other.data[i]);.

Ihr "deep copy" -Konstruktor, PositionListname__, ist im Grunde ein Kopierkonstruktor, obwohl der Copy-Konstruktor tendenziell eine tiefe Kopie anzeigt, so dass der Parameter deepCopynicht erforderlich ist.

1
Thomas

Hier ist eine Funktion, die ich benutze:

function copy(arr) {
  return arr
    .map(x => Object
      .keys(x)
      .reduce((acc, y) => {
        acc[y] = x[y]
        return acc
      }, {}))
}

Es funktioniert nur bei Arrays mit Objekten auf einer Ebene.

0
Kainan