web-dev-qa-db-ja.com

オブジェクト配列のディープコピー

コンストラクタを使用してオブジェクト配列のディープコピーを作成したいのですが。

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];
            }

しかし、私が上記の理由で何らかの理由で機能していません。私が実行した自動テストと、それらのテストに失敗しました。だから、ここにエラーがあります。

13
Snowman

あなたが実装したのはshallowコピーです。 deepコピーを実装するには、変更する必要があります

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

other.data[i]の__copydata[i]に割り当てるものに。これを行う方法は、Positionクラスによって異なります。可能な選択肢は次のとおりです。

  • コピーコンストラクタ:

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

  • ファクトリーメソッド:

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

  • クローン:

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

ノート:

  1. 上記は、コピーコンストラクタ、ファクトリメソッド、およびクローンメソッドが、Positionクラスに応じて、それぞれ「正しい」種類のコピーを実装していることを前提としています。下記参照。
  2. cloneアプローチは、Positionが明示的にサポートしている場合にのみ機能し、これは一般に劣ったソリューションと見なされます。さらに、cloneのネイティブ実装(つまり、Object.clone()メソッド)は浅いコピーを行うことに注意する必要があります。

実際、Javaにディープコピーを実装することの一般的な問題は複雑です。Positionクラスの場合、属性はすべてプリミティブ型(たとえば、intまたはしたがって、参照コピーがある場合は、コピーコンストラクター/ファクトリーメソッド/クローンメソッドを使用して、必要な種類のコピーを実行する必要があります。また、一般的なケース(サイクルを処理する必要がある場合)は難しく、各クラスに特別なメソッドを実装する必要があります。

オブジェクトの配列をコピーするには、他に潜在的な方法が1つあります。配列内のオブジェクトがserializableである場合、ObjectOutputStreamおよびObjectInputStream serializeを使用してオブジェクトをコピーし、配列を逆シリアル化できます。しかしながら:

  • これは高いです
  • オブジェクトが(推移的に)シリアライズ可能である場合にのみ機能します。
  • transientフィールドの値はコピーされません。

シリアル化によるコピーは推奨されません。クローニングや他の方法をサポートする方が良いでしょう。

全体として、Javaではディープコピーを回避するのが最善です。

最後に、Positionクラスのコピーコンストラクターが機能することについての質問に答えるには、次のようになると思います。

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

@Turtleが言うように、魔法は関与していません。既存のインスタンスからコピーして状態を初期化するコンストラクターを(手動で)実装します。

24
Stephen C

あなたが言う時:

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

参照のリストをコピーしているだけです(これがオブジェクトの配列であると想定しています)。詳細コピーを作成する場合は、newを使用して、配列内の各オブジェクトの新しいインスタンスを作成する必要があります。

2
Justin Ethier

言う代わりに:

data[i]=other.data[i]

Positionのコピーコンストラクター(つまり、別のPositionを取り込んでその内部のプリミティブデータをコピーするPositionのコンストラクター)を作成し、data[i]=new Position(other.data[i]);と言います。

基本的に「ディープコピー」コンストラクターPositionListはコピーコンストラクターですが、コピーコンストラクターはディープコピーを示す傾向があるため、deepCopyパラメーターは不要です。

1
Thomas

これが私が使用する関数です:

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

単一レベルのオブジェクトを持つ配列でのみ機能します。

0
Cazanator