Java 逆引き集 | Arrays.copyOf / copyOfRange — 配列操作と拡張

Java Java
スポンサーリンク

Arrays.copyOf / copyOfRange — 配列操作と拡張

配列を「丸ごとコピー」「サイズ変更」「一部だけ取り出す」を、安全に簡潔に書けるのが Arrays.copyOfArrays.copyOfRange。初心者がつまずきやすい「インデックスの含まれる/含まれない」「サイズを超えるとどうなる」を、例とテンプレートで分かりやすく整理します。


基本の考え方

  • Arrays.copyOf(original, newLength): 配列を新しい長さでコピー。先頭から newLength 要素分だけ新配列に詰める。足りない分は型の初期値で埋まる(intなら0、booleanならfalse、参照型ならnull)。
  • Arrays.copyOfRange(original, from, to): from(含む)〜to(含まない)の範囲をコピー。to - from 要素の新配列になる。範囲が元配列の末尾を越えた不足分は初期値で埋まる。
  • 不変な性質: 元配列は変更されない(新しい配列を返す)。
  • 型を保つ: 元の配列の型に合わせた新配列が返る(プリミティブ・参照型ともにOK)。

すぐ試せる基本例

1) サイズを変えてコピー(copyOf)

import java.util.Arrays;

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

// 3→5に拡張(不足分は0で埋まる)
int[] b = Arrays.copyOf(a, 5);
System.out.println(Arrays.toString(b)); // [1, 2, 3, 0, 0]

// 3→2に縮小(先頭2つだけ残る)
int[] c = Arrays.copyOf(a, 2);
System.out.println(Arrays.toString(c)); // [1, 2]
Java

2) 範囲指定で取り出す(copyOfRange)

import java.util.Arrays;

int[] a = {10, 20, 30, 40, 50};

// [from=1, to=4) → 1,2,3番目を取得
int[] slice = Arrays.copyOfRange(a, 1, 4);
System.out.println(Arrays.toString(slice)); // [20, 30, 40]
Java

3) 範囲外まで取り出すと…(不足分は初期値)

import java.util.Arrays;

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

// to が配列長を越える(初期値補充)
int[] extended = Arrays.copyOfRange(a, 1, 6);
System.out.println(Arrays.toString(extended)); // [2, 3, 0, 0, 0]
Java

参照型・文字列でも同じように使える

import java.util.Arrays;

String[] names = {"Tanaka", "Sato", "Ito"};

// 先頭2件だけコピー
String[] first2 = Arrays.copyOf(names, 2);        // ["Tanaka", "Sato"]

// 1..3 を範囲コピー
String[] mid = Arrays.copyOfRange(names, 1, 3);   // ["Sato", "Ito"]

// 長くコピー(不足は null)
String[] padded = Arrays.copyOf(names, 5);        // ["Tanaka", "Sato", "Ito", null, null]
Java
  • 参照型の不足分は null になる点に注意。

よくある用途とレシピ

配列の「拡張」代わりに使う

int[] buf = new int[3];
buf[0]=1; buf[1]=2; buf[2]=3;

// 要素が増える見込みで拡張
buf = Arrays.copyOf(buf, 6); // 末尾に余白ができる(0で埋まる)
Java

部分抽出(スライス)

double[] data = {0.5, 1.2, 3.4, 5.6, 7.8};
double[] window = Arrays.copyOfRange(data, 2, 5); // [3.4, 5.6, 7.8]
Java

片側だけ欲しい(先頭N/末尾N)

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

// 先頭3件
int[] head = Arrays.copyOf(a, 3);

// 末尾3件
int n = 3;
int[] tail = Arrays.copyOfRange(a, Math.max(0, a.length - n), a.length);
Java

落とし穴と回避策

  • インデックスの意味違い(fromは含む、toは含まない):
    • よくある誤りは「toも含む」と思い込むこと。常に to は「含まない」ので、欲しい最後の位置は lastIndex + 1 を指定する。
  • 負のインデックスや from > to:
    • from < 0from > to は例外。範囲を必ず検証する。
    • to が大きすぎる場合は初期値で埋まるが、from が大きすぎると長さ0や例外になりやすい。
  • 巨大コピーのメモリ負荷:
    • 大きく拡張するとヒープを圧迫。必要サイズを見積もる、分割して処理するなどの対策を。
  • 深いコピーが必要なとき:
    • 2次元配列や内部参照を持つ配列の「完全コピー」が必要なら、要素ごとにコピーする。copyOf は「1階層」のコピー。

テンプレート集(そのまま使える形)

  • サイズ指定コピー
T[] dst = Arrays.copyOf(src, newLength);
Java
  • 範囲コピー(from 含む、to 含まない)
T[] dst = Arrays.copyOfRange(src, from, to);
Java
  • 末尾N件の取得
int n = ...;
T[] tail = Arrays.copyOfRange(src, Math.max(0, src.length - n), src.length);
Java
  • 安全なスライス(境界チェック付き)
int from = ...; int to = ...;
from = Math.max(0, from);
to = Math.min(src.length, to);
if (from > to) to = from;
T[] slice = Arrays.copyOfRange(src, from, to);
Java

例題で身につける

例題1: CSV行の先頭3列だけ抜き出し

import java.util.Arrays;

String[] cols = "A,B,C,D,E".split(",");
String[] first3 = Arrays.copyOf(cols, 3);
System.out.println(Arrays.toString(first3)); // [A, B, C]
Java

例題2: ログのローリングバッファ(末尾N件を取得)

import java.util.Arrays;

String[] logs = {"L1","L2","L3","L4","L5"};
int n = 3;
String[] recent = Arrays.copyOfRange(logs, Math.max(0, logs.length - n), logs.length);
System.out.println(Arrays.toString(recent)); // [L3, L4, L5]
Java

例題3: 2次元配列の「浅いコピー」と「深いコピー」

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

// 浅いコピー(行配列への参照は共有される)
int[][] shallow = Arrays.copyOf(grid, grid.length);

// 深いコピー(各行もコピー)
int[][] deep = new int[grid.length][];
for (int i = 0; i < grid.length; i++) {
    deep[i] = Arrays.copyOf(grid[i], grid[i].length);
}
Java

まとめ

  • copyOf は「サイズ指定で先頭からコピー」、copyOfRange は「範囲指定(from含む/to含まない)」で部分抽出。足りない分は型の初期値で埋まる。
  • 参照型は不足分が null、プリミティブは型の初期値になる。範囲とサイズの境界を正しく扱えば、安全に配列の拡張・スライスが書ける。
  • 2次元以上の構造では必要に応じて「深いコピー」を選び、メモリ負荷と境界チェックに気を配ると失敗しない。
タイトルとURLをコピーしました