web-dev-qa-db-ja.com

配列をコピーする

私は常に更新されている配列aを持っています。 a = [1,2,3,4,5]としましょう。 aを正確に複製し、それをbと呼ぶ必要があります。 a[6,7,8,9,10]に変更する場合、bはまだ[1,2,3,4,5]であるべきです。これを行うための最良の方法は何ですか?私はforループを試してみました:

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

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

306
badcoder

System.arraycopy() を使用して試すことができます。

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

System.arraycopy( src, 0, dest, 0, src.length );
520
Bala R

あなたが使用することができます

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

同様に。

216
MeBigFatGuy

コピーを作成する場合は、

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
166

/からの良い説明 http://www.journaldev.com/753/how-to-copy-arrays-in-Java

Java配列のコピー方法

Object.clone() :Objectクラスはclone()メソッドを提供します。Javaの配列もObjectなので、このメソッドを使用して完全な配列コピーを実現できます。この方法は、アレイの部分コピーが必要な場合には適していません。

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

Arrays.copyOf(): 配列の最初のいくつかの要素または配列のフルコピーをコピーする場合は、このメソッドを使用できます。明らかにSystem.arraycopy()のように汎用性はありませんが、混乱を招くこともなく使いやすいこともありません。

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

48

これらの「配列をコピーするためのより良い方法」すべてが実際にあなたの問題を解決するわけではないと私は感じています。

あなたは言う

[...]のようなforループを試しましたが、それは正しく動作していないようですか?

そのループを見ると、 明白な理由はありません それが機能しないためです...

  • abの配列が混乱している(例えばabが同じ配列を参照している)場合、または
  • あなたはアプリケーションがマルチスレッドであり、異なるスレッドが同時にa配列を読み込んで更新しています。

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

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

(あなたの質問には、これが本当にスレッドに関連していると思うように私に導くヒントがあります。例えば、aは絶えず変化しているというあなたのステートメント。)

30
Stephen C

JavaでArrays.copyOf()を使ってみることができます。

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

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を使用しています。

:を参照

7

配列から長さを呼び出すすべての解決策は、あなたのコードを追加します。

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コモンズあなたが見つけることができます そこ

7
Cherry

ArrayListではなく生の配列を操作しなければならない場合は、Arraysに必要なものがあります。ソースコードを見れば、これらは配列のコピーを取得するための絶対的に最良の方法です。 System.arraycopy()メソッドは、論理的でないパラメータを指定すると、多くの未チェックの例外をスローするので、それらにはかなりの防御的プログラミングがあります。

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

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

コピーが指定された長さになるように、指定された配列をコピーし、切り捨てるか(必要に応じて)NULLで埋めます。元の配列とコピーの両方で有効なすべてのインデックスに対して、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を除く)。元の配列内の後続の要素からの値は、コピー内の後続の要素に配置されます。 from(以上)でなければならない範囲(to)の最後のインデックスは、original.lengthより大きくすることができます。この場合、インデックスがoriginal以上のコピーのすべての要素にnullが配置されます。長さ - から。返される配列の長さはto-fromになります。結果の配列は、元の配列とまったく同じクラスです。

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 は配列をコピーするための絶対的な最速の方法です。

1
user177800

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

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