Java での配列コピーの詳細な分析: System.arraycopy、Arrays.copyOf、Arrays.copyOfRange

Java で配列を操作する場合、選択できるメソッドは多数あり、その中には や などがありSystem.arraycopy()ますこれらのメソッドを使用すると、異なる配列間でデータをコピーできますが、それらの間には微妙な違いがいくつかあります。このブログ投稿では、これらのメソッドをいつ使用するか、どのように正しく使用するかを理解できるように、これらのメソッドについて詳しく説明します。Arrays.copyOf()Arrays.copyOfRange()

System.arraycopy()

System.arraycopyメソッドは Java のネイティブ メソッドであり、その実際の実装は Java 仮想マシンの基礎となる実装によって提供されます。

public static native void arraycopy(Object src,  int  srcPos,
                                    Object dest, int destPos,
                                    int length);

パラメータの説明:

  • src: ソース配列
  • srcPos: ソース配列内の開始位置
  • dest: ターゲット配列
  • destPos: ターゲット配列内の開始位置
  • 長さ: コピーされる要素の数

System.arraycopy()このメソッドのパフォーマンスは、基礎となるコードによって実装され、ハードウェアの特性を利用して高速にデータをコピーできるため、非常に高くなります。通常、ループを使用して配列要素を 1 つずつコピーするよりもはるかに高速です。

System.arraycopy()アップキャストまたはダウンキャストに使用できますが、データ型の互換性と実行時の型チェックを確保するために注意して使用する必要があります。データ型が一致しない場合、コンパイルはできても実行時に実行時例外が発生しますjava.lang.ArrayStoreExceptionベスト プラクティスは、コードを明確で保守しやすい状態に保つために、不必要な型変換を避けることです。

例:

public static void main(String[] args) {

    String[] strArray = new String[]{"xj1","xj2","xj3","xj4","xj5"};
    String[] strArrayCopy = new String[5];
    System.arraycopy(strArray,0,strArrayCopy,0,3);

    //向下转型
    TestEntity[] testArray = new TestChildEntity[]{new TestChildEntity("xiuji","xj")} ;
    TestChildEntity[] testChildArrayCopy = new TestChildEntity[2];

    System.arraycopy(testArray,0,testChildArrayCopy,0,1);

    System.out.println(Arrays.toString(strArrayCopy));
    System.out.println(Arrays.toString(testChildArrayCopy));
}

演算結果

[xj1, xj2, xj3, null, null]
[TestChildEntity{nickName='xj'name='xiuji'}, null]

Arrays.copyOf()

文法:

型変換なし

copyOf(T[] original, int newLength)

変換タイプ

copyOf(U[] original, int newLength, Class<? extends T[]> newType)

パラメータの説明:

  • Original: コピーされる元の配列。
  • newLength: 新しい配列の長さ。元の配列の長さより長くても短くても構いません。
  • newType: 新しい配列の型。これは Class オブジェクトで、通常は配列クラスです。これは、新しい配列のタイプを決定するために使用されます。

ソースコード:

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
     
    @SuppressWarnings("unchecked")
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);这是一个三元条件运算符,
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}
  • @SuppressWarnings("unchecked"): この注釈は、未チェックの変換警告を無視するようにコンパイラーに指示するために使用されます。このメソッドでは型変換が行われるため、このアノテーションを使用して警告を抑制します。

  • T[] copy = ((Object)newType == (Object)Object[].class): この 3 項条件演算子は、newType の型に基づいて新しい配列のコピーを作成します。newType が Object[].class の場合は、Object 型の新しい配列を作成します。それ以外の場合は、Array.newInstance() メソッドを使用して、newType のコンポーネントの型によって型が決定される新しい配列を作成します。

  • System.arraycopy(original, 0, copy, 0,Math.min(original.length, newLength)): System.arraycopy() メソッドは、元の配列の要素を新しい配列にコピーするために使用されます。そのパラメータには、元の配列、元の配列の開始位置 (0 は最初の要素から開始することを意味します)、ターゲット配列 (つまり、新しい配列)、ターゲット配列の開始位置 (0 は最初の要素から開始することを意味します) が含まれます。位置)、およびコピーする要素の数。元の配列の長さと newLength の小さい方によって決まります。

例:

public static void main(String[] args) {

    String[] strArray = new String[]{"xj1","xj2","xj3","xj4","xj5"};
    String[] strArrayCopy = Arrays.copyOf(strArray,8);

    //向下转型
    TestEntity[] testArray = new TestChildEntity[]{new TestChildEntity("xiuji","xj")} ;
    TestChildEntity[] testChildArrayCopy =Arrays.copyOf(testArray,3,TestChildEntity[].class);

    System.out.println(Arrays.toString(strArrayCopy));
    System.out.println(Arrays.toString(testChildArrayCopy));

}

操作結果:

[xj1, xj2, xj3, xj4, xj5, null, null, null]
[TestChildEntity{nickName='xj'name='xiuji'}, null, null]

Arrays.copyOfRange()

文法:

型変換なし

copyOfRange(U[] original, int from, int toe) 

変換タイプ

copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType) 
  • Original: これは、要素がコピーされる元の配列です。
  • from: これは、コピーされる範囲の開始インデックスです。
  • to: コピーされる範囲の終了インデックス (排他的) です。
  • newType: これは新しい配列のタイプで、通常は配列クラスです。

ソースコード:

public static <T,U> T[] copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType) {
    int newLength = to - from;
    if (newLength < 0)
        throw new IllegalArgumentException(from + " > " + to);
    @SuppressWarnings("unchecked")
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    System.arraycopy(original, from, copy, 0,
                     Math.min(original.length - from, newLength));
    return copy;
}
  • int newLength = to - from;: この行は、指定された from インデックスと to インデックスに基づいて、新しい配列の長さを計算します。

  • if (newLength < 0) throw new IllegalArgumentException(from + " > " + to);: この行は、newLength が負であるかどうか (つまり、from が to より大きいかどうか) をチェックします。この条件が満たされると、from インデックスが to インデックスより大きいことを示す IllegalArgumentException がスローされます。

  • @SuppressWarnings("unchecked"): このアノテーションは、未チェックの型変換の警告を抑制するために使用されます。

  • T[] copy = ((Object)newType == (Object)Object[].class) ? (T[]) new Object[newLength] : (T[]) Array.newInstance(newType.getComponentType(), newLength);:
    この行は、指定された newType に基づいて新しい配列を作成します。リフレクションを使用して、必要な型の配列を作成します。
    newType が Object[].class と等しい場合、newLength の長さで新しい Object 配列が作成されます。
    それ以外の場合は、Array.newInstance を使用して、コンポーネント タイプが newType のコンポーネント タイプと同じで、長さが newLength である新しい配列を作成します。これにより、特定の型の配列を作成できるようになります。

  • System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength));:
    この行は System.arraycopy を使用して実際の配列コピー操作を実行します。元の配列の from インデックスから始まる要素をコピー配列の 0 インデックスまでコピーします。コピーする要素の数は Math.min(original.length - from, newLength) によって決定され、指定された範囲のみがコピーされることが保証されます。

例:

public static void main(String[] args) {

    String[] strArray = new String[]{"xj1","xj2","xj3","xj4","xj5"};
    String[] strArrayCopy = Arrays.copyOfRange(strArray,2,4);

    //向下转型
    TestEntity[] testArray = new TestChildEntity[]{new TestChildEntity("xiuji","xj")} ;
    TestChildEntity[] testChildArrayCopy =Arrays.copyOfRange(testArray,0,1,TestChildEntity[].class);

    System.out.println(Arrays.toString(strArrayCopy));
    System.out.println(Arrays.toString(testChildArrayCopy));

}

操作結果:

[xj3, xj4]
[TestChildEntity{nickName='xj'name='xiuji'}]

予防

配列コピーを使用する場合は、次の点に注意する必要があります。

  • 新しい配列の長さがソース配列の長さより短い場合、新しい配列はソース配列の最初のいくつかの要素を切り捨てます。

  • ソース配列内の要素がオブジェクト参照である場合、新しい配列内の要素は引き続き同じオブジェクトを参照します。つまり、新しい配列への変更がソース配列に影響を与える可能性があります。

  • ソース配列に基本データ型 (int、char など) が含まれている場合、新しい配列には、これらの基本データ型のデフォルト値 (0 や '\0' など) が含まれます

要約する

配列を操作する場合、適切なコピー方法の選択は、特定のニーズに応じて異なります。これらの方法の使用に関するいくつかの提案を次に示します。

  • 効率的な低レベルのコピー操作と、要素の開始位置と数を手動で計算する機能が必要な場合は、System.arraycopy が良い選択になる可能性があります。
  • Arrays.copyOf は、ソース配列と同じ長さの新しい配列を作成し、ソース配列の内容全体を新しい配列にコピーする場合に便利なオプションです。
  • ソース配列の一部を新しい配列にコピーする必要がある場合は、Arrays.copyOfRange が最適です。

どちらの方法を選択しても、配列を操作する際の柔軟性、効率性、安全性が向上します。この記事が、これらの配列コピー方法の理解と使用に役立つことを願っています。

おすすめ

転載: blog.csdn.net/weixin_44002151/article/details/132789577