Как скопировать массив в Java

Как скопировать массив в Java

1. Overviewс

В этой быстрой статье мы обсудим различные методы копирования массивов в 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, который мы видели ранее. В основном он предоставляет два метода:copyOf(…) иcopyRangeOf(…).

Давайте посмотрим наcopyOf первый:

int[] array = {23, 43, 55, 12};
int newLength = array.length;

int[] copiedArray = Arrays.copyOf(array, newLength);

Важно отметить, что классArrays используетMath.min(…) для выбора минимальной длины исходного массива и значения нового параметра длины для определения размера результирующего массива.

Arrays.copyOfRange() принимает 2 параметра, "from' и"to', в дополнение к параметру исходного массива. Результирующий массив включает индекс «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]);

Оба эти методаdo a shallow copy объектов, если они применяются к массиву непримитивных типов объектов. Давайте посмотрим на пример тестового случая:

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

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

С другой стороны, если мы клонируем массив не примитивных типов, используя тот же метод, то результаты будут другими.

Он создаетa shallow copy элементов массива непримитивного типа, даже если класс закрытого объекта реализует интерфейсCloneable и переопределяет методclone() из классаObject.

Давайте посмотрим на пример:

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. Использование APIStream

Оказывается, мы можем использовать Stream API для копирования массивов. Давайте посмотрим на пример:

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

Для не примитивных типов это также сделает поверхностную копию объектов. Чтобы узнать больше оJava 8 Streams, вы можете запуститьhere.

6. Внешние библиотеки

Apache Commons 3 предлагает служебный класс под названиемSerializationUtils, который предоставляет методclone(…). Это очень полезно, если нам нужно сделать глубокую копию массива непримитивных типов. Его можно загрузить сhere, и его зависимость от Maven:


    org.apache.commons
    commons-lang3
    3.5

Давайте посмотрим на тестовый пример:

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. С точки зрения производительности, это медленнее, чем методы-клоны, написанные вручную для каждого из объектов в нашем графе объектов для копирования.

7. Заключение

В этом уроке мы рассмотрели различные варианты копирования массива в Java.

Используемый метод в основном зависит от точного сценария. Пока мы используем массив примитивных типов, мы можем использовать любой из методов, предлагаемых классамиSystem иArrays. Никакой разницы в производительности быть не должно.

Для непримитивных типов, если нам нужно сделать глубокую копию массива, мы можем либо использоватьSerializationUtils, либо явно добавить методы клонирования в наши классы.

И, как всегда, примеры, показанные в этой статье, доступны наover on GitHub.