配列のコピーを作成する


345

a常に更新されている配列があります。としましょうa = [1,2,3,4,5]。の完全なコピーを作成しaて呼び出す必要がありますbaに変更した場合でも[6,7,8,9,10]、変更するb必要があります[1,2,3,4,5]。これを行う最良の方法は何ですか?私は次のforようなループを試しました:

for(int i=0; i<5; i++) {
    b[i]=a[i]
}

しかし、それは正しく動作していないようです。ディープコピーなどの高度な用語は使用しないでください。意味がわかりません。

回答:


558

あなたはSystem.arraycopy()を使って試すことができます

int[] src  = new int[]{1,2,3,4,5};
int[] dest = new int[5];

System.arraycopy( src, 0, dest, 0, src.length );

しかし、おそらくほとんどの場合clone()を使用する方が良いでしょう。

int[] src = ...
int[] dest = src.clone();

9
ホイールを再出現させないための+1。そして、私が知る限り、このソリューションは配列のコピーを高速化できます。
フェリペフンメル

6
cloneとarraycopyはどちらもネイティブです。クローンの方がわずかに速いと思います。違いが重要であるということではありません。
MeBigFatGuy

5
@ Felipe、@ MeBigFatGuy-大規模な配列の場合のみ。小さいアレイの場合、セットアップのオーバーヘッドのため、コピーループの方が高速になる場合があります。のjavadocをSystem.arraycopy見ると、メソッドが開始する前にさまざまなことを確認する必要があることがわかります。これらのチェックの一部は、静的配列タイプに応じて、コピーループでは不要です。
スティーブンC

7
@ FelipeHummel、@ MeBigFatGuy、@ StephenC-ここに回答で言及されている配列コピーメソッドのパフォーマンステストがあります。そのセットアップでclone()は、250 000要素の中で最速であることが判明しました。
アダム

6
ここでのすべての議論がマイクロパフォーマンスの問題に関するものであり、99.999%の時間は重要ではないことを確認するのは残念です。より重要な点はsrc.clone()、新しい配列を割り当てて実行するよりも読みやすく、エラーの可能性がはるかに少ないことですarraycopy。(また、たまたま高速です。)
ブライアンゲッツ

231

あなたは使うことができます

int[] a = new int[]{1,2,3,4,5};
int[] b = a.clone();

同様に。


6
Aが[6,7,8,9,10]に変更されたとしても、Bは[1,2,3,4,5]であるはずです」というOPのポイントを明確にしています。OPはループの使用を試みたがうまくいかなかったと語った。
ハリージョイ

15
キャストは不要です。優れた静的アナライザーはそれについて警告します。しかし、クローン作成は、アレイの新しいコピーを作成するための最良の方法です。
エリクソン

5
@MeBigFatGuy-OPのユースケースでは、同じ配列に繰り返しコピーする必要があるため、クローンは機能しません。
スティーブンC

4
@スティーブンC、私はそれを読みませんでした。私は彼がコピーを望んでいることを読んだだけで、その後、非隠しバージョンを繰り返し更新します。
MeBigFatGuy

4
@MeBigFatGuy- 「私は常に更新されている配列Aを持っている」と彼は言った。多分それを読みすぎているかもしれませんが、これは彼が繰り返しAからBにもコピーしていることを意味していると考えています。
スティーブンC

184

次のコピーを作成する場合:

int[] a = {1,2,3,4,5};

これは行く方法です:

int[] b = Arrays.copyOf(a, a.length);

Arrays.copyOfa.clone()小さなアレイよりも高速かもしれません。どちらの要素も同等に高速ですが、clone()は戻りますObjectので、コンパイラーはへの暗黙のキャストを挿入する必要がありint[]ます。次のようなバイトコードで確認できます。

ALOAD 1
INVOKEVIRTUAL [I.clone ()Ljava/lang/Object;
CHECKCAST [I
ASTORE 2

62

http://www.journaldev.com/753/how-to-copy-arrays-in-javaからの素晴らしい説明

Java配列のコピーメソッド

Object.clone():Objectクラスはclone()メソッドを提供します配列もオブジェクトであるため、このメソッドを使用して配列全体をコピーできます。この方法は、配列の部分的なコピーが必要な場合には適していません。

System.arraycopy():システムクラスarraycopy()は、配列の部分的なコピーを行うための最良の方法です。コピーする要素の総数と、コピー元とコピー先の配列のインデックス位置を簡単に指定できます。たとえば、System.arraycopy(source、3、destination、2、5)は、ソースの3番目のインデックスから宛先の2番目のインデックスまで、ソースから宛先に5つの要素をコピーします。

Arrays.copyOf():配列の最初のいくつかの要素または配列の完全なコピーをコピーする場合は、このメソッドを使用できます。明らかにSystem.arraycopy()のように用途が広いわけではありませんが、紛らわしくなく、使いやすいです。

Arrays.copyOfRange():開始インデックスが0でない場合に、配列のいくつかの要素をコピーする場合は、このメソッドを使用して部分配列をコピーできます。


35

これらの「配列をコピーするためのより良い方法」のすべてが本当にあなたの問題を解決するつもりはないと感じています。

あなたは言う

[...]のようなforループを試してみましたが、正しく機能していないようです。

そのループを見ると、機能しないことの明らかな理由はありません...

  • どういうわけかab配列と配列がめちゃくちゃになっている(たとえば、同じ配列ab参照している)、または
  • アプリケーションがマルチスレッド化されており、さまざまなスレッドがa配列の読み取りと更新を同時に行っている。

どちらの場合も、コピーを実行する別の方法では根本的な問題を解決できません。

最初のシナリオの修正は明らかです。2番目のシナリオでは、スレッドを同期するいくつかの方法を理解する必要があります。アトミック配列クラスにはアトミックコピーコンストラクターやクローンメソッドがないため、役に立ちませんが、プリミティブミューテックスを使用して同期するとうまくいきます。

(あなたの質問には、これが確かにスレッドに関連していると私に思わせるヒントがあります。例えば、あなたのステートメントaは常に変化しています。)


2
同意した..おそらく本当。
MeBigFatGuy


9

配列から長さを呼び出すすべてのソリューションで、コードに冗長なnullチェッカーを追加して例を検討してください:

int[] a = {1,2,3,4,5};
int[] b = Arrays.copyOf(a, a.length);
int[] c = a.clone();

//What if array a comes as local parameter? You need to use null check:

public void someMethod(int[] a) {
    if (a!=null) {
        int[] b = Arrays.copyOf(a, a.length);
        int[] c = a.clone();
    }
}

ホイールを発明せず、必要なすべてのチェックがすでに実行されているユーティリティクラスを使用することをお勧めします。Apache CommonsのArrayUtilsを検討してください。コードが短くなります:

public void someMethod(int[] a) {
    int[] b = ArrayUtils.clone(a);
}

そこにある Apache commons


8

も使用できますArrays.copyOfRange

public static void main(String[] args) {
    int[] a = {1,2,3};
    int[] b = Arrays.copyOfRange(a, 0, a.length);
    a[0] = 5;
    System.out.println(Arrays.toString(a)); // [5,2,3]
    System.out.println(Arrays.toString(b)); // [1,2,3]
}

このメソッドはに似ていますがArrays.copyOf、より柔軟です。どちらも内部で使用しますSystem.arraycopy

を参照してください


3

配列のnullセーフコピーの場合Object.clone()、この回答で提供されているメソッドでオプションを使用することもできます

int[] arrayToCopy = {1, 2, 3};
int[] copiedArray = Optional.ofNullable(arrayToCopy).map(int[]::clone).orElse(null);

このソリューションは複雑すぎますが、メモリの浪費も発生します。また、配列にシークレット(たとえば、パスワード付きのバイト配列)が含まれている場合は、ガベージコレクションが公開されるまで中間オブジェクトがヒープに常駐するため、セキュリティ上の欠陥も発生します。攻撃者に。
Weltraumschaf

1
配列がこのコンストラクトのために特別にヒープ上にあることに同意しません。実際、必要なときにのみcloneを呼び出し、Optionalオブジェクトは既存の配列への参照を持つ空のオブジェクトです。パフォーマンスへの影響については、このタイプの構成はJVM内のインライン化に適しているため、他のメソッドよりも影響が少ないため、実際に影響があるとは言えません。それをより複雑であると考えるかどうかは、スタイルの問題(関数型プログラミングと手続き型プログラミングだけではなく)の問題です。
Nicolas Henneaux、

3

あなたがいない生の配列と協力し、必要がある場合はArrayList、次にArrays何が必要があります。ソースコードを見ると、これらは配列のコピーを取得するのに最適な方法です。System.arraycopy()非論理的なパラメーターを与えると、メソッドがチェックされない例外をたくさんスローするので、防御的なプログラミングには十分です。

Arrays.copyOf()最初のNth要素から新しい短い配列にコピーするいずれかを使用できます。

public static <T> T[] copyOf(T[] original, int newLength)

指定された配列をコピーし、(必要に応じて)ヌルで切り捨てまたはパディングして、コピーが指定された長さになるようにします。元の配列とコピーの両方で有効なすべてのインデックスについて、2つの配列には同じ値が含まれます。コピーでは有効であるが元のインデックスでは有効でないインデックスの場合、コピーにはnullが含まれます。そのようなインデックスは、指定された長さが元の配列の長さより大きい場合にのみ存在します。結果の配列は、元の配列とまったく同じクラスです。

2770
2771    public static <T,U> T[] More ...copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
2772        T[] copy = ((Object)newType == (Object)Object[].class)
2773            ? (T[]) new Object[newLength]
2774            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
2775        System.arraycopy(original, 0, copy, 0,
2776                         Math.min(original.length, newLength));
2777        return copy;
2778    }

またはArrays.copyOfRange()、トリックも行います:

public static <T> T[] copyOfRange(T[] original, int from, int to)

指定された配列の指定された範囲を新しい配列にコピーします。範囲(from)の初期インデックスは、0とoriginal.lengthの間にある必要があります。original [from]の値は、コピーの最初の要素に配置されます(from == original.lengthまたはfrom == toを除く)。元の配列の後続の要素の値は、コピーの後続の要素に配置されます。範囲(to)の最終インデックスは、from以上である必要がありますが、original.lengthよりも大きい場合があります。この場合、nullは、インデックスがoriginal以上のコピーのすべての要素に配置されます。長さ-から。返される配列の長さは-からです。結果の配列は、元の配列とまったく同じクラスです。

3035    public static <T,U> T[] More ...copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType) {
3036        int newLength = to - from;
3037        if (newLength < 0)
3038            throw new IllegalArgumentException(from + " > " + to);
3039        T[] copy = ((Object)newType == (Object)Object[].class)
3040            ? (T[]) new Object[newLength]
3041            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
3042        System.arraycopy(original, from, copy, 0,
3043                         Math.min(original.length - from, newLength));
3044        return copy;
3045    }

ご覧のとおり、これらはどちらも、System.arraycopy実行しようとしていることが有効であることを示す防御ロジックを備えたラッパー関数です。

System.arraycopy 配列をコピーする最も速い方法です。


0

2D配列で同様の問題が発生し、ここで終了しました。メイン配列をコピーして内部配列の値を変更していましたが、両方のコピーで値が変更されたときは驚きました。基本的に両方のコピーは独立していたが、同じ内部配列への参照が含まれていたため、必要なものを取得するために内部配列のコピーの配列を作成する必要がありました。

それはおそらくOPの問題ではありませんが、それでも役立つことを願っています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.