W3docs

Копирование массивов в Java

Копирование массивов в Java: System.arraycopy, Arrays.copyOf, clone и потоки.

Присваивание одной переменной-массива другой не копирует данные — оно просто создаёт два имени, указывающих на один и тот же массив:

int[] a = {1, 2, 3};
int[] b = a;
b[0] = 99;
System.out.println(a[0]);   // 99 — same array

Чтобы получить независимую копию, нужно явно её запросить. Java предлагает несколько способов, каждый из которых подходит для своей ситуации: полное дублирование, изменение размера при копировании, извлечение среза, копирование между двумя существующими массивами. Большинство из них находятся в утилитарном классе Arrays, поэтому в примерах ниже предполагается наличие import java.util.Arrays;.

Arrays.copyOf — дублирование или изменение размера

Принимает исходный массив и целевую длину. Если новая длина совпадает, вы получаете чистую копию. Если она больше, дополнительные ячейки заполняются значениями по умолчанию (0, false, null); если меньше — хвост отбрасывается.

import java.util.Arrays;

int[] data = {1, 2, 3, 4, 5};
int[] same   = Arrays.copyOf(data, data.length);   // {1, 2, 3, 4, 5}
int[] grown  = Arrays.copyOf(data, 8);             // {1, 2, 3, 4, 5, 0, 0, 0}
int[] shrunk = Arrays.copyOf(data, 3);             // {1, 2, 3}

Именно так «увеличивают» массив фиксированного размера — выделяют более крупный и копируют в него данные.

Arrays.copyOfRange — срез

Копирует полуоткрытый диапазон [from, to):

int[] data = {1, 2, 3, 4, 5};
int[] middle = Arrays.copyOfRange(data, 1, 4);   // {2, 3, 4}

Значение to может превышать длину исходного массива — дополнительные ячейки заполняются значениями по умолчанию. Таким образом, срез и дополнение можно выполнить за один вызов.

System.arraycopy — копирование в существующий массив

Это примитив самого низкого уровня: копирует диапазон одного массива в диапазон другого. Не выделяет новую память, не возвращает значение — оба массива должны уже существовать.

int[] src = {10, 20, 30, 40, 50};
int[] dst = new int[8];
System.arraycopy(src, 1, dst, 3, 3);
// dst is now {0, 0, 0, 20, 30, 40, 0, 0}

Аргументы: (src, srcStart, dst, dstStart, length). Используйте этот метод, когда нужно поместить данные по заданному смещению или переместить элементы внутри одного массива — например, System.arraycopy(arr, i, arr, i + 1, length - i - 1) сдвигает срез вправо на один, освобождая место для вставки. Перекрывающиеся диапазоны внутри одного массива поддерживаются и обрабатываются корректно.

clone() — быстрое полное копирование

Каждый массив имеет метод clone(), который возвращает новый массив той же длины с теми же содержимым:

int[] data = {1, 2, 3};
int[] copy = data.clone();

Для одномерных массивов clone() — самый короткий и наглядный способ создать дубликат. Тип возвращаемого значения совпадает с исходным — приведение типов не требуется.

Для многомерных массивов clone() является поверхностным: он копирует внешний массив, но строки по-прежнему остаются исходными ссылками:

int[][] grid = {{1, 2}, {3, 4}};
int[][] shallow = grid.clone();
shallow[0][0] = 99;
System.out.println(grid[0][0]);   // 99 — same inner row

Чтобы выполнить глубокое копирование двумерного массива, нужно клонировать каждую строку отдельно:

int[][] deep = new int[grid.length][];
for (int r = 0; r < grid.length; r++) deep[r] = grid[r].clone();

Вариант с потоком: Arrays.stream(grid).map(int[]::clone).toArray(int[][]::new).

Потоки

Для массивов объектов Arrays.stream(arr).toArray(T[]::new) создаёт копию:

String[] names = {"Ada", "Linus", "Grace"};
String[] copy = Arrays.stream(names).toArray(String[]::new);

Для массивов примитивов используйте специализированный поток:

import java.util.stream.IntStream;

int[] data = {1, 2, 3, 4, 5};
int[] copy = IntStream.of(data).toArray();

Потоки требуют больше ресурсов, чем copyOf или clone(), и применяются главным образом для случаев преобразования.map(...) или .filter(...) в середине цепочки — а не для простого дублирования.

Ссылочные и глубокие копии

Для примитивов каждая копия полностью независима — дублируются сами значения. Для массивов объектов все рассмотренные методы копирования являются поверхностными: они дублируют массив ссылок, но каждая ссылка по-прежнему указывает на тот же объект, что и в оригинале.

StringBuilder[] src = { new StringBuilder("hi") };
StringBuilder[] copy = src.clone();
copy[0].append(" there");
System.out.println(src[0]);   // "hi there" — same StringBuilder

Обычно это не проблема — Stringами, Integerами и любыми неизменяемыми объектами можно безопасно делиться. Если элементы изменяемы и нужны независимые объекты, придётся копировать их вручную:

StringBuilder[] deep = new StringBuilder[src.length];
for (int i = 0; i < src.length; i++) deep[i] = new StringBuilder(src[i]);

Выбор метода копирования

Что нужноИспользовать
Точный дубликат одномерного массиваarr.clone() или Arrays.copyOf(arr, arr.length)
Изменение размера при копировании (увеличить/уменьшить)Arrays.copyOf(arr, newLength)
Срез — копирование части массиваArrays.copyOfRange(arr, from, to)
Копирование в ячейку существующего массиваSystem.arraycopy(...)
Глубокое копирование двумерного массиваклонировать каждую строку в цикле
Копирование с преобразованиемпотоки (map, filter, затем toArray)

Рабочий пример

java— editable, runs on the server

Что дальше

Теперь вы умеете создавать, обходить, искать, сортировать и копировать массивы — это полный набор инструментов для работы с контейнерами фиксированного размера в Java. Следующая часть посвящена методам: как упаковывать код в именованные многократно используемые единицы, принимающие параметры и возвращающие значения.

Практика

Практика
Что возвращает arr.clone() для int[][]?
Что возвращает arr.clone() для int[][]?
Was this page helpful?