Javaで配列をコピーする方法

Javaで配列をコピーする方法

1. Overview

この簡単な記事では、Javaでのさまざまな配列コピー方法について説明します。 配列のコピーは些細なタスクのように思えるかもしれませんが、慎重に行わないと予期しない結果やプログラムの動作を引き起こす可能性があります。

2. Systemクラス

コアJavaライブラリ–System.arrayCopy()から始めましょう。これにより、配列がソース配列から宛先配列にコピーされ、指定された長さまでソース位置からターゲット位置へのコピーアクションが開始されます。

ターゲット配列にコピーされる要素の数は、指定された長さに等しくなります。 配列のサブシーケンスを別のサブシーケンスにコピーする簡単な方法を提供します。

配列引数のいずれかがnull,の場合、NullPointerExceptionをスローし、整数引数のいずれかが負または範囲外の場合、IndexOutOfBoundExceptionをスローします。

java.util.Systemクラスを使用して完全な配列を別の配列にコピーする例を見てみましょう。

int[] array = {23, 43, 55};
int[] copiedArray = new int[3];

System.arraycopy(array, 0, copiedArray, 0, 3);

このメソッドが取る引数は次のとおりです。ソース配列、ソース配列からコピーする開始位置、宛先配列、宛先配列の開始位置、およびコピーする要素の数。

サブシーケンスをソース配列から宛先にコピーすることを示す別の例を見てみましょう。

int[] array = {23, 43, 55, 12, 65, 88, 92};
int[] copiedArray = new int[3];

System.arraycopy(array, 2, copiedArray, 0, 3);
assertTrue(3 == copiedArray.length);
assertTrue(copiedArray[0] == array[2]);
assertTrue(copiedArray[1] == array[3]);
assertTrue(copiedArray[2] == array[4]);

3. Arraysクラス

Arraysクラスは、配列を別の配列にコピーするための複数のオーバーロードされたメソッドも提供します。 内部的には、以前に見たSystemクラスによって提供されるのと同じアプローチを使用します。 これは主に、copyOf(…)copyRangeOf(…)の2つのメソッドを提供します。

copyOfの最初の:を見てみましょう

int[] array = {23, 43, 55, 12};
int newLength = array.length;

int[] copiedArray = Arrays.copyOf(array, newLength);

ArraysクラスはMath.min(…)を使用して、ソース配列の長さの最小値を選択し、新しい長さパラメーターの値を使用して、結果の配列のサイズを決定することに注意してください。

Arrays.copyOfRange()は、ソース配列パラメーターに加えて、 ‘from'と‘to'の2つのパラメーターを取ります。 結果の配列には ‘from'インデックスが含まれますが、‘to'インデックスは除外されます。 例を見てみましょう:

int[] array = {23, 43, 55, 12, 65, 88, 92};

int[] copiedArray = Arrays.copyOfRange(array, 1, 4);
assertTrue(3 == copiedArray.length);
assertTrue(copiedArray[0] == array[1]);
assertTrue(copiedArray[1] == array[2]);
assertTrue(copiedArray[2] == array[3]);

非プリミティブオブジェクトタイプの配列に適用された場合、これらのメソッドは両方ともオブジェクトのdo a shallow copyです。 テストケースの例を見てみましょう。

Employee[] copiedArray = Arrays.copyOf(employees, employees.length);

employees[0].setName(employees[0].getName() + "_Changed");

assertArrayEquals(copiedArray, array);

結果は浅いコピーであるため、元の配列の要素の従業員名を変更すると、コピー配列が変更されます。

したがって、「非プリミティブ型のディープコピーを行いたい場合は、今後のセクションで説明する他のオプションに進むことができます。

4. Object.clone()を使用した配列コピー

Object.clone()は、配列内のObjectクラスから継承されます。

まず、cloneメソッドを使用してプリミティブ型の配列をコピーしましょう。

int[] array = {23, 43, 55, 12};

int[] copiedArray = array.clone();

そして、それが機能するという証拠:

assertArrayEquals(copiedArray, array);
array[0] = 9;

assertTrue(copiedArray[0] != array[0]);

上記の例は、クローン作成後のコンテンツは同じですが、参照が異なることを示しています。したがって、いずれかを変更しても、もう一方には影響しません。

一方、同じメソッドを使用して非プリミティブ型の配列を複製すると、結果は異なります。

囲まれたオブジェクトのクラスがCloneableインターフェイスを実装し、Objectクラスのclone()メソッドをオーバーライドする場合でも、非プリミティブ型の配列要素のa shallow copyを作成します。

例を見てみましょう。

public class Address implements Cloneable {
    // ...

    @Override
    protected Object clone() throws CloneNotSupportedException {
         super.clone();
         Address address = new Address();
         address.setCity(this.city);

         return address;
    }
}

アドレスの新しい配列を作成し、clone()メソッドを呼び出すことで、実装をテストできます。

Address[] addresses = createAddressArray();
Address[] copiedArray = addresses.clone();
addresses[0].setCity(addresses[0].getCity() + "_Changed");
assertArrayEquals(copiedArray, addresses);

この例は、囲まれたオブジェクトがCloneableの場合でも、元の配列またはコピーされた配列を変更すると、もう一方の配列も変更されることを示しています。

5. StreamAPIの使用

配列のコピーにもStream APIを使用できます。 例を見てみましょう。

String[] strArray = {"orange", "red", "green'"};
String[] copiedArray = Arrays.stream(strArray).toArray(String[]::new);

非プリミティブ型の場合、オブジェクトの浅いコピーも行います。 Java 8 Streamsの詳細については、hereを開始できます。

6. 外部ライブラリ

Apache Commons 3は、clone(…)メソッドを提供するSerializationUtilsと呼ばれるユーティリティクラスを提供します。 非プリミティブ型の配列のディープコピーを行う必要がある場合に非常に便利です。 hereからダウンロードでき、Mavenの依存関係は次のとおりです。


    org.apache.commons
    commons-lang3
    3.5

テストケースを見てみましょう。

public class Employee implements Serializable {
    // fields
    // standard getters and setters
}

Employee[] employees = createEmployeesArray();
Employee[] copiedArray = SerializationUtils.clone(employees);
employees[0].setName(employees[0].getName() + "_Changed");
assertFalse(
  copiedArray[0].getName().equals(employees[0].getName()));

このクラスでは、各オブジェクトがSerializableインターフェイスを実装する必要があります。 パフォーマンスの観点では、コピーするオブジェクトグラフの各オブジェクトに対して手動で記述されたクローンメソッドよりも遅くなります。

7. 結論

このチュートリアルでは、Javaで配列をコピーするためのさまざまなオプションを見ました。

使用する方法は、主に正確なシナリオに依存します。 プリミティブ型の配列を使用している限り、SystemクラスとArraysクラスが提供する任意のメソッドを使用できます。 パフォーマンスに違いはないはずです。

非プリミティブ型の場合、配列のディープコピーを実行する必要がある場合は、SerializationUtilsを使用するか、クラスにクローンメソッドを明示的に追加できます。

そしていつものように、この記事に示されている例はover on GitHubで利用できます。