Comment copier un tableau en Java

1. Vue d'ensemble

Dans cet article rapide, nous aborderons différentes méthodes de copie de tableaux en Java. La copie de tableau peut sembler être une tâche triviale, mais elle peut entraîner des résultats et des comportements inattendus du programme si elle n'est pas effectuée avec soin.

2. La classe système

Commençons par la bibliothèque Java principale - System.arrayCopy () ; ceci copie un tableau d'un tableau source vers un tableau de destination, en commençant l'action de copie de la position source vers la position cible jusqu'à la longueur spécifiée.

Le nombre d'éléments copiés dans le tableau cible est égal à la longueur spécifiée. Il fournit un moyen simple de copier une sous-séquence d'un tableau dans un autre.

Si l'un des arguments du tableau est nul, il lève une exception NullPointerException et si l'un des arguments d'entier est négatif ou hors limites, il lève une exception IndexOutOfBoundException .

Jetons un œil à un exemple pour copier un tableau complet dans un autre en utilisant la classe java.util.System :

int[] array = {23, 43, 55}; int[] copiedArray = new int[3]; System.arraycopy(array, 0, copiedArray, 0, 3);

Les arguments que cette méthode prend sont; un tableau source, la position de départ à copier à partir du tableau source, un tableau de destination, la position de départ dans le tableau de destination et le nombre d'éléments à copier.

Jetons un coup d'œil à un autre exemple qui montre la copie d'une sous-séquence d'un tableau source vers une destination:

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. La classe Arrays

La classe Arrays propose également plusieurs méthodes surchargées pour copier un tableau dans un autre. En interne, il utilise la même approche fournie par la classe System que celle que nous avons vue précédemment. Il fournit principalement deux méthodes, copyOf (…) et copyRangeOf (…) .

Jetons un coup d'œil à copyOf en premier :

int[] array = {23, 43, 55, 12}; int newLength = array.length; int[] copiedArray = Arrays.copyOf(array, newLength); 

Il est important de noter que la classe Arrays utilise Math.min (…) pour sélectionner le minimum de la longueur du tableau source et la valeur du nouveau paramètre de longueur afin de déterminer la taille du tableau résultant.

Arrays.copyOfRange () prend 2 paramètres, « from» et « to» en plus du paramètre du tableau source. Le tableau résultant inclut l' index ' from' mais l'index 'to' est exclu. Voyons un exemple:

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

Ces deux méthodes effectuent une copie superficielle des objets si elles sont appliquées à un tableau de types d'objets non primitifs. Voyons un exemple de cas de test:

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

Étant donné que le résultat est une copie superficielle, une modification du nom d'employé d'un élément du tableau d'origine a provoqué la modification du tableau de copie.

Et donc - si nous voulons faire une copie complète des types non primitifs - nous pouvons opter pour les autres options décrites dans les sections à venir.

4. Copie de tableau avec Object.clone ()

Object.clone () est hérité de la classe Object dans un tableau.

Copions d'abord un tableau de types primitifs en utilisant la méthode clone:

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

Et une preuve que cela fonctionne:

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

L'exemple ci-dessus montre qu'ils ont le même contenu après le clonage mais qu'ils contiennent des références différentes, donc toute modification de l'une d'elles n'affectera pas l'autre.

D'un autre côté, si nous clonons un tableau de types non primitifs en utilisant la même méthode, les résultats seront différents.

Il crée une copie superficielle des éléments du tableau de type non primitif, même si la classe de l'objet inclus implémente l' interface Cloneable et remplace la méthode clone () de la classe Object .

Jetons un œil à un exemple:

public class Address implements Cloneable { // ... @Override protected Object clone() throws CloneNotSupportedException { super.clone(); Address address = new Address(); address.setCity(this.city); return address; } } 

We can test our implementation by creating a new array of addresses and invoking our clone() method:

Address[] addresses = createAddressArray(); Address[] copiedArray = addresses.clone(); addresses[0].setCity(addresses[0].getCity() + "_Changed"); 
assertArrayEquals(copiedArray, addresses);

This example shows that any change in the original or copied array would cause the change in the other one even when the enclosed objects are Cloneable.

5. Using the Stream API

It turns out, we can use the Stream API for copying arrays too. Let's have a look at an example:

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

For the non-primitive types, it will also do a shallow copy of objects. To learn more about Java 8 Streams, you can start here.

6. External Libraries

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.

La méthode à utiliser dépend principalement du scénario exact. Tant que nous utilisons un tableau de type primitif, nous pouvons utiliser l'une des méthodes proposées par les classes System et Arrays . Il ne devrait y avoir aucune différence de performance.

Pour les types non primitifs, si nous devons faire une copie complète d'un tableau, nous pouvons soit utiliser SerializationUtils, soit ajouter des méthodes de clonage à nos classes explicitement.

Et comme toujours, les exemples présentés dans cet article sont disponibles sur over sur GitHub.