Cómo copiar una matriz en Java

1. Información general

En este artículo rápido, discutiremos diferentes métodos de copia de matrices en Java. La copia de matriz puede parecer una tarea trivial, pero puede causar resultados inesperados y comportamientos del programa si no se realiza con cuidado.

2. La clase de sistema

Comencemos con la biblioteca central de Java: System.arrayCopy () ; esto copia una matriz de una matriz de origen a una matriz de destino, comenzando la acción de copia desde la posición de origen a la posición de destino hasta la longitud especificada.

El número de elementos copiados en la matriz de destino es igual a la longitud especificada. Proporciona una forma sencilla de copiar una subsecuencia de una matriz a otra.

Si alguno de los argumentos de la matriz es nulo, arroja una NullPointerException y si alguno de los argumentos enteros es negativo o está fuera de rango, arroja una IndexOutOfBoundException .

Echemos un vistazo a un ejemplo para copiar una matriz completa a otra usando la clase java.util.System :

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

Los argumentos que toma este método son; una matriz de origen, la posición inicial para copiar desde la matriz de origen, una matriz de destino, la posición inicial en la matriz de destino y la cantidad de elementos que se copiarán.

Echemos un vistazo a otro ejemplo que muestra cómo copiar una subsecuencia de una matriz de origen a un destino:

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 clase Arrays

La clase Arrays también ofrece múltiples métodos sobrecargados para copiar una matriz a otra. Internamente, utiliza el mismo enfoque proporcionado por la clase System que hemos visto anteriormente. Proporciona principalmente dos métodos, copyOf (…) y copyRangeOf (…) .

Echemos un vistazo a copyOf primero :

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

Es importante tener en cuenta que la clase Arrays usa Math.min (…) para seleccionar el mínimo de la longitud de la matriz de origen y el valor del nuevo parámetro de longitud para determinar el tamaño de la matriz resultante.

Arrays.copyOfRange () toma 2 parámetros, ' from' y ' to' además del parámetro de matriz de origen. La matriz resultante incluye el índice ' desde' pero el índice 'hasta' está excluido. Veamos un ejemplo:

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

Ambos métodos hacen una copia superficial de los objetos si se aplican a una matriz de tipos de objetos no primitivos. Veamos un caso de prueba de ejemplo:

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

Debido a que el resultado es una copia superficial, un cambio en el nombre de empleado de un elemento de la matriz original provocó el cambio en la matriz de copia.

Y así, si queremos hacer una copia profunda de tipos no primitivos, podemos optar por las otras opciones descritas en las próximas secciones.

4. Copia de matriz con Object.clone ()

Object.clone () se hereda de la clase Object en una matriz.

Primero copiemos una matriz de tipos primitivos usando el método de clonación:

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

Y una prueba de que funciona:

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

El ejemplo anterior muestra que tienen el mismo contenido después de la clonación pero tienen referencias diferentes, por lo que cualquier cambio en cualquiera de ellos no afectará al otro.

Por otro lado, si clonamos una matriz de tipos no primitivos utilizando el mismo método, los resultados serán diferentes.

Crea una copia superficial de los elementos de la matriz de tipos no primitivos, incluso si la clase del objeto adjunto implementa la interfaz Cloneable y anula el método clone () de la clase Object .

Echemos un vistazo a un ejemplo:

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

Podemos probar nuestra implementación creando una nueva matriz de direcciones e invocando nuestro método clone () :

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

Este ejemplo muestra que cualquier cambio en el arreglo original o copiado causaría el cambio en el otro incluso cuando los objetos incluidos son Clonables .

5. Usando la API Stream

Resulta que también podemos usar la API Stream para copiar matrices. Echemos un vistazo a un ejemplo:

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

Para los tipos no primitivos, también hará una copia superficial de los objetos. Para obtener más información sobre Java 8 Streams , puede comenzar aquí.

6. Bibliotecas externas

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.

El método a utilizar depende principalmente del escenario exacto. Siempre que estemos usando una matriz de tipos primitivos, podemos usar cualquiera de los métodos ofrecidos por las clases System y Arrays . No debería haber ninguna diferencia en el rendimiento.

Para tipos no primitivos, si necesitamos hacer una copia profunda de una matriz, podemos usar SerializationUtils o agregar métodos de clonación a nuestras clases explícitamente.

Y como siempre, los ejemplos que se muestran en este artículo están disponibles en GitHub.