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

1. Общ преглед

В тази бърза статия ще обсъдим различни методи за копиране на масиви в Java. Копирането на масив може да изглежда като тривиална задача, но може да доведе до неочаквани резултати и програмно поведение, ако не се направи внимателно.

2. Система Class

Нека започнем с основната библиотека на Java - System.arrayCopy () ; това копира масив от масив източник в целеви масив, като започва действието за копиране от позицията източник до целевата позиция до определената дължина.

Броят на елементите, копирани в целевия масив, е равен на определената дължина. Той осигурява лесен начин за копиране на под-последователност от масив в друг.

Ако някой от аргументите на масива е нула, той изхвърля 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. Масивите Class

Класът 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 параметъра, „ от“ и „ до“ в допълнение към параметъра на масива източник. Полученият масив включва индекса „ от“ , но индексът „до“ е изключен. Да видим пример:

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

И двата метода правят плитко копие на обекти, ако се прилагат върху масив от непримитивни типове обекти. Да видим един примерен тестов случай:

Employee[] copiedArray = Arrays.copyOf(employees, employees.length); employees[0].setName(employees[0].getName() + "_Changed"); assertArrayEquals(copiedArray, array);

Тъй като резултатът е плитко копие - промяната в името на служителя на елемент от оригиналния масив е причинила промяната в масива за копиране.

И така - ако искаме да направим дълбоко копие на непримитивни типове - можем да отидем за другите опции, описани в предстоящите раздели.

4. Копиране на масив с Object.clone ()

Object.clone () се наследява от клас Object в масив.

Нека първо копираме масив от примитивни типове, използвайки метод на клониране:

int[] array = {23, 43, 55, 12}; int[] copiedArray = array.clone(); 

И доказателство, че работи:

assertArrayEquals(copiedArray, array); array[0] = 9; assertTrue(copiedArray[0] != array[0]);

Горният пример показва, че имат същото съдържание след клониране, но те съдържат различни препратки, така че всяка промяна в някоя от тях няма да засегне другата.

От друга страна, ако клонираме масив от непримитивни типове, използвайки същия метод, тогава резултатите ще бъдат различни.

Той създава плитко копие на елементите от масив от непримитивен тип, дори ако класът на затворения обект реализира интерфейса 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. Използване на Stream API

Оказва се, че можем да използваме Stream API и за копиране на масиви. Нека да разгледаме един пример:

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

За непримитивните типове той също ще направи плитко копие на обекти. За да научите повече за Java 8 Streams , можете да започнете от тук.

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

Apache Commons 3 offers a utility class called SerializationUtils that provides a clone(…) method. It is very useful if we need to do a deep copy of an array of non-primitive types. It can be downloaded from here and its Maven dependency is:

 org.apache.commons commons-lang3 3.5  

Let's have a look at a test case:

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

This class requires that each object should implement the Serializable interface. In terms of performance, it is slower than the clone methods written manually for each of the objects in our object graph to copy.

7. Conclusion

In this tutorial, we had a look at the various options to copy an array in Java.

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

За непримитивни типове, ако трябва да направим дълбоко копие на масив, можем да използваме SerializationUtils или да добавим клониращи методи към нашите класове изрично.

И както винаги, примерите, показани в тази статия, са достъпни в GitHub.