Метод System.arraycopy()
Метод System.arraycopy() копирует часть одного массива в другой массив (или в тот же самый) быстрее и надёжнее, чем ручной цикл for. Метод статический, объявлен в классе java.lang.System и реализован как native — то есть его тело написано не на Java, а на уровне JVM, что и даёт основной выигрыш в скорости.
Сигнатура и параметры метода
Метод имеет единственную сигнатуру и работает с массивами любого типа — примитивов и объектов:
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length) | Параметр | Тип | Значение |
|---|---|---|
src | Object (массив) | Массив-источник, из которого копируются элементы |
srcPos | int | Индекс, с которого начинается копирование в источнике |
dest | Object (массив) | Массив-приёмник, в который копируются элементы |
destPos | int | Индекс, с которого элементы записываются в приёмник |
length | int | Количество копируемых элементов |
Метод ничего не возвращает (void) и не создаёт новый массив — приёмник dest должен существовать и быть достаточно длинным ещё до вызова.
Пример: копирование части массива в другой массив
Рассмотрим пример, копирующий элементы 2, 3, 4 из массива arraySource в массив arrayDestination:
import java.util.Arrays;
public class ArrayCopyExample1 {
public static void main(String[] args) {
int[] arraySource = {1, 2, 3, 4, 5, 6};
int[] arrayDestination = {0, 0, 0, 0, 0, 0, 0, 0};
System.out.println("arraySource: " + Arrays.toString(arraySource));
System.out.println("arrayDestination: "
+ Arrays.toString(arrayDestination));
System.arraycopy(arraySource, 1, arrayDestination, 2, 3);
System.out.println("arrayDestination after arrayCopy: "
+ Arrays.toString(arrayDestination));
}
}
Результат выполнения:
arraySource: [1, 2, 3, 4, 5, 6]
arrayDestination: [0, 0, 0, 0, 0, 0, 0, 0]
arrayDestination after arrayCopy: [0, 0, 2, 3, 4, 0, 0, 0] Вызов System.arraycopy(arraySource, 1, arrayDestination, 2, 3) означает: взять 3 элемента из arraySource, начиная с индекса 1 (это значения 2, 3, 4), и записать их в arrayDestination, начиная с индекса 2. Длины массивов при этом могут различаться — важно лишь, чтобы в обоих хватало места для указанного диапазона.
Копирование в тот же массив с перекрытием областей
Можно копировать в тот же массив, причём даже если исходный и целевой диапазоны перекрываются:
import java.util.Arrays;
public class ArrayCopyExample2 {
public static void main(String[] args) {
int[] array = {1, 2, 3, 4, 5, 6, 7, 8};
System.out.println(Arrays.toString(array));
System.arraycopy(array, 1, array, 3, 3);
System.out.println(Arrays.toString(array));
}
}
Результат выполнения:
[1, 2, 3, 4, 5, 6, 7, 8]
[1, 2, 3, 2, 3, 4, 7, 8] Важно
System.arraycopy() корректно обрабатывает перекрывающиеся диапазоны — элементы сначала как бы копируются во временную область, поэтому не перезатирают друг друга раньше времени. Наивный цикл for, копирующий элементы по одному в порядке возрастания индекса, на таком же перекрытии даст неверный результат: часть исходных значений будет затёрта ещё до того, как их успеют прочитать.
Исключения: когда arraycopy() выбрасывает ошибку
Метод не изменяет размеры массивов и не проверяет типы «на лету» так, как это делает обычный Java-код, поэтому часть проверок он выполняет сам и выбрасывает исключения:
| Исключение | Когда возникает |
|---|---|
NullPointerException | src или dest равны null |
ArrayIndexOutOfBoundsException | srcPos, destPos или length отрицательны, либо диапазон srcPos + length или destPos + length выходит за пределы соответствующего массива |
ArrayStoreException | элемент из src нельзя записать в dest из-за несовместимости типов (например, копирование Object[] с элементами String в Integer[]) |
Пример на ArrayIndexOutOfBoundsException — длина length больше, чем помещается в приёмник:
int[] src = {1, 2, 3};
int[] dest = new int[3];
try {
System.arraycopy(src, 0, dest, 0, 5); // в dest нет места для 5 элементов
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Поймали: " + e.getMessage());
} Пример на NullPointerException — приёмник не инициализирован:
int[] src = {1, 2, 3};
int[] dest = null;
try {
System.arraycopy(src, 0, dest, 0, 3);
} catch (NullPointerException e) {
System.out.println("dest равен null");
} Важная деталь: если при копировании до возникновения исключения часть элементов уже была перенесена, эти изменения в dest останутся — метод не откатывает уже выполненную часть операции.
System.arraycopy() и другие способы копирования массива
| Способ | Создаёт новый массив? | Особенности |
|---|---|---|
System.arraycopy(...) | Нет | Копирует часть (или весь) массив в уже существующий приёмник, в том числе с перекрытием диапазонов |
Arrays.copyOf(array, newLength) | Да | Создаёт новый массив нужной длины и копирует элементы; внутри использует System.arraycopy() |
array.clone() | Да | Полная поверхностная копия массива той же длины и типа |
Цикл for | По ситуации | Ручное поэлементное копирование: медленнее на больших массивах и легко ошибиться при перекрывающихся диапазонах |
Выбирайте System.arraycopy(), когда приёмник уже есть и нужно скопировать в него только часть данных (или сдвинуть элементы внутри одного массива). Если же нужен именно новый массив — короче и понятнее использовать Arrays.copyOf() или clone(); оба они не отменяют arraycopy(), а построены поверх него.
Про сами массивы и работу с ними — в уроках «Одномерные массивы», «Многомерные массивы» и «Длина массива».
Производительность: почему arraycopy быстрее цикла
System.arraycopy() объявлен как native — JVM выполняет копирование напрямую в памяти (аналогично низкоуровневым операциям вроде memmove), минуя накладные расходы обычного байткода: проверку границ на каждой итерации, инкремент счётчика, вызовы. Именно поэтому этот метод — стандартный выбор внутри самой библиотеки классов Java: на нём построены Arrays.copyOf(), Arrays.copyOfRange() и внутренняя реализация многих коллекций.
На небольших массивах разница с ручным циклом незаметна, но на больших объёмах данных (десятки тысяч элементов и больше) System.arraycopy() и построенные на нём методы заметно быстрее и — благодаря корректной обработке перекрытий — надёжнее самодельного цикла.
Часто задаваемые вопросы
Что делает System.arraycopy()?
Копирует заданное количество элементов из одного массива в другой (или в тот же самый), начиная с указанных индексов источника и приёмника. Метод статический, находится в классе java.lang.System, ничего не возвращает и не создаёт новый массив — приёмник должен существовать заранее.
Какие исключения бросает System.arraycopy() и когда?
NullPointerException — если источник или приёмник равны null; ArrayIndexOutOfBoundsException — если индексы или длина отрицательны либо диапазон выходит за пределы массива; ArrayStoreException — если элементы источника несовместимы по типу с приёмником.
Чем System.arraycopy() отличается от Arrays.copyOf()?
System.arraycopy() копирует в уже существующий массив и может копировать только часть данных. Arrays.copyOf() удобнее, когда нужен именно новый массив: он сам создаёт массив нужной длины и внутри вызывает System.arraycopy().
Можно ли скопировать массив сам в себя?
Да, и это безопасно даже при перекрывающихся диапазонах индексов: System.arraycopy() обрабатывает такое копирование корректно, в отличие от наивного цикла for, который может затереть ещё не прочитанные элементы.
System.arraycopy() быстрее цикла for?
Как правило да, особенно на больших массивах: метод реализован как native и выполняет копирование на уровне JVM, минуя накладные расходы обычного байткода. На небольших массивах разница в производительности практически незаметна.
Видео объяснение
Предпочитаете видеоформат? Посмотрите этот урок с примерами и объяснениями.
Комментарии