1.

概要

このクイック記事では、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

クラスによって提供されるのと同じアプローチを使用します。それは主に2つのメソッド、

copyOf(…​)



copyRangeOf(…​)

を提供します。

最初の

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

これらのメソッドは両方とも、非プリミティブオブジェクト型の配列に適用された場合、オブジェクトの

浅いコピー

を行います。テストケースの例を見てみましょう。

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()

メソッドをオーバーライドしても、非プリミティブ型配列要素の

浅いコピー

を作成します。

例を見てみましょう。

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

Stream

APIを使用する

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

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

非プリミティブ型の場合は、オブジェクトの浅いコピーも行います。


Java 8 Streams

についてさらに学ぶために、

ここ

を始めることができます。

** 6. 外部ライブラリ


Apache Commons 3



clone(…​)

メソッドを提供する

SerializationUtils

と呼ばれるユーティリティクラスを提供します。非プリミティブ型の配列の詳細コピーを作成する必要がある場合には非常に便利です。

here

からダウンロードできます。 Mavenの依存関係は次のとおりです。

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.5</version>
</dependency>

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

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

インタフェースを実装する必要があります。パフォーマンスの面では、オブジェクトグラフ内の各オブジェクトに対して手動で作成したcloneメソッドをコピーするよりも時間がかかります。


7. 結論

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

使用する方法は、主に正確なシナリオによって異なります。プリミティブ型配列を使用している限り、

System

クラスと

Arrays

クラスで提供されている任意のメソッドを使用できます。パフォーマンスに違いはありません。

非プリミティブ型の場合、配列のディープコピーを行う必要がある場合は、

SerializationUtils

を使用するか、明示的にクラスにクローンメソッドを追加することができます。

そしていつものように、この記事で示されている例はhttps://github.com/eugenp/tutorials/tree/master/core-java-arrays[GitHubで利用可能]で入手可能です。