Hacer copia de una matriz

345

Tengo una matriz aque se actualiza constantemente. Digamos Vamos a = [1,2,3,4,5]. Necesito hacer una copia duplicada exacta de ay llamarlo b. Si tuviera aque cambiar a [6,7,8,9,10], btodavía debería ser [1,2,3,4,5]. ¿Cuál es la mejor manera de hacer esto? Intenté un forbucle como:

for(int i=0; i<5; i++) {
    b[i]=a[i]
}

pero eso no parece funcionar correctamente. No utilice términos avanzados como copia profunda, etc., porque no sé lo que eso significa.

badcoder
fuente

Respuestas:

558

Puede intentar usar System.arraycopy ()

int[] src  = new int[]{1,2,3,4,5};
int[] dest = new int[5];

System.arraycopy( src, 0, dest, 0, src.length );

Pero, probablemente sea mejor usar clone () en la mayoría de los casos:

int[] src = ...
int[] dest = src.clone();
Bala R
fuente
99
+1 por no reactivar la rueda. Y hasta donde yo sé, esta solución es la más rápida que puede obtener en la copia de matriz.
Felipe Hummel
66
Tanto el clon como la matriz son nativos. Esperaría que el clon sea marginalmente más rápido. No es que la diferencia importe.
MeBigFatGuy
55
@Felipe, @MeBigFatGuy: solo para una gran variedad. Para una matriz pequeña, un bucle de copia puede ser más rápido debido a los gastos generales de configuración. Si observa el javadoc System.arraycopy, verá que el método necesita verificar varias cosas antes de que comience. Algunas de estas comprobaciones son innecesarias con un bucle de copia, dependiendo de los tipos de matriz estática.
Stephen C
77
@FelipeHummel, @MeBigFatGuy, @StephenC: aquí hay una prueba de rendimiento de los métodos de copia de matriz mencionados en las respuestas aquí. En esa configuración, clone()resulta ser el más rápido para 250 000 elementos.
Adam
66
Es decepcionante ver que toda la discusión aquí es sobre problemas de micro rendimiento, que el 99.999% de las veces, no importan. El punto más importante es que src.clone()es más legible y tiene muchas menos oportunidades de error que asignar una nueva matriz y hacer arraycopy. (Y también es rápido.)
Brian Goetz
231

puedes usar

int[] a = new int[]{1,2,3,4,5};
int[] b = a.clone();

también.

MeBigFatGuy
fuente
66
Solo estoy aclarando el punto del OP que dice : " Si A cambiara a [6,7,8,9,10], B aún debería ser [1,2,3,4,5] ". OP dijo que intentó usar el bucle pero no funcionó para él.
Harry Joy el
15
El reparto es innecesario; Un buen analizador estático lo advertirá. Pero la clonación es definitivamente la mejor manera de hacer una nueva copia de una matriz.
erickson el
55
@MeBigFatGuy: el caso de uso del OP implica una copia repetida en la misma matriz, por lo que el clon no funciona.
Stephen C
44
@ Stephen C, no leí eso. Acabo de leer que quiere una copia, y posteriormente actualizaré repetidamente la versión no almacenada.
MeBigFatGuy
44
@MeBigFatGuy - dijo "Tengo una matriz A que se actualiza constantemente". . Tal vez estoy leyendo demasiado sobre eso, pero considero que esto implica que él también está copiando repetidamente A a B.
Stephen C
184

Si quieres hacer una copia de:

int[] a = {1,2,3,4,5};

Este es el camino a seguir:

int[] b = Arrays.copyOf(a, a.length);

Arrays.copyOfpuede ser más rápido que a.clone()en arreglos pequeños. Ambos elementos de copia son igualmente rápidos pero clone () regresa, Objectpor lo que el compilador tiene que insertar una conversión implícita en int[]. Puede verlo en el código de bytes, algo como esto:

ALOAD 1
INVOKEVIRTUAL [I.clone ()Ljava/lang/Object;
CHECKCAST [I
ASTORE 2
Evgeniy Dorofeev
fuente
62

Buena explicación de http://www.journaldev.com/753/how-to-copy-arrays-in-java

Métodos de copia de matriz de Java

Object.clone () : la clase Object proporciona el método clone () y dado que la matriz en java también es un Object, puede usar este método para lograr una copia completa de la matriz. Este método no le conviene si desea una copia parcial de la matriz.

System.arraycopy () : la clase de sistema arraycopy () es la mejor manera de hacer una copia parcial de una matriz. Le proporciona una manera fácil de especificar el número total de elementos para copiar y las posiciones del índice de la matriz de origen y destino. Por ejemplo, System.arraycopy (fuente, 3, destino, 2, 5) copiará 5 elementos de origen a destino, comenzando desde el tercer índice de origen hasta el segundo índice de destino.

Arrays.copyOf (): si desea copiar los primeros elementos de una matriz o una copia completa de la matriz, puede utilizar este método. Obviamente no es tan versátil como System.arraycopy () pero tampoco es confuso y fácil de usar.

Arrays.copyOfRange () : si desea que se copien pocos elementos de una matriz, donde el índice inicial no es 0, puede usar este método para copiar la matriz parcial.

Kanagavelu Sugumar
fuente
35

Tengo la sensación de que todas estas "mejores formas de copiar una matriz" realmente no van a resolver su problema.

Tu dices

Intenté un bucle for como [...] pero parece que no funciona correctamente?

Mirando ese ciclo, no hay una razón obvia para que no funcione ... a menos que:

  • de alguna manera tiene las matrices ay bdesordenadas (por ejemplo, ay se brefiere a la misma matriz), o
  • su aplicación es multiproceso y diferentes hilos leen y actualizan la amatriz simultáneamente.

En cualquier caso, las formas alternativas de copiar no resolverán el problema subyacente.

La solución para el primer escenario es obvia. Para el segundo escenario, tendrá que encontrar alguna forma de sincronizar los hilos. Las clases de matriz atómica no ayudan porque no tienen constructores de copia atómica o métodos de clonación, pero la sincronización usando un mutex primitivo hará el truco.

(Hay indicios en su pregunta que me llevan a pensar que esto realmente está relacionado con el hilo; por ejemplo, su declaración que aestá cambiando constantemente).

Stephen C
fuente
2
de acuerdo ... probablemente cierto.
MeBigFatGuy
17

Puede intentar usar Arrays.copyOf () en Java

int[] a = new int[5]{1,2,3,4,5};
int[] b = Arrays.copyOf(a, a.length);
Durgaprasad Nagarkatte
fuente
3
Redundante: stackoverflow.com/a/15962949/139985 dijo lo mismo.
Stephen C
9

Todas las soluciones que llaman la longitud de la matriz, agregue su código redundante nulos checkersconsider ejemplo:

int[] a = {1,2,3,4,5};
int[] b = Arrays.copyOf(a, a.length);
int[] c = a.clone();

//What if array a comes as local parameter? You need to use null check:

public void someMethod(int[] a) {
    if (a!=null) {
        int[] b = Arrays.copyOf(a, a.length);
        int[] c = a.clone();
    }
}

Le recomiendo que no invente la rueda y use la clase de utilidad donde ya se han realizado todas las verificaciones necesarias. Considere ArrayUtils de apache commons. Su código se acorta:

public void someMethod(int[] a) {
    int[] b = ArrayUtils.clone(a);
}

Apache comunes que puedes encontrar allí

Cereza
fuente
8

También puedes usar Arrays.copyOfRange.

Ejemplo :

public static void main(String[] args) {
    int[] a = {1,2,3};
    int[] b = Arrays.copyOfRange(a, 0, a.length);
    a[0] = 5;
    System.out.println(Arrays.toString(a)); // [5,2,3]
    System.out.println(Arrays.toString(b)); // [1,2,3]
}

Este método es similar a Arrays.copyOf, pero es más flexible. Ambos se usan System.arraycopydebajo del capó.

Ver :

RUMANIA_ingeniero
fuente
3

Para una copia segura de nulo de una matriz, también puede usar un opcional con el Object.clone()método proporcionado en esta respuesta .

int[] arrayToCopy = {1, 2, 3};
int[] copiedArray = Optional.ofNullable(arrayToCopy).map(int[]::clone).orElse(null);
Nicolas Henneaux
fuente
A pesar de que esta solución está demasiado complicada, también introduce desperdicio de memoria y si la matriz contiene un secreto (por ejemplo, una matriz de bytes con contraseña) también presenta fallas de seguridad porque los objetos intermedios residirán en el montón hasta que se pueda exponer la recolección de basura. a los atacantes
Weltraumschaf
1
No estoy de acuerdo en que la matriz estará en el montón especialmente para esta construcción. De hecho, llama clon solo cuando es necesario y el Optionalobjeto es solo un objeto vacío con una referencia a la matriz existente. Sobre el impacto en el rendimiento, diría que es prematuro decir que en realidad es un impacto ya que este tipo de construcción es un buen candidato para alinearse dentro de la JVM y luego no es más impacto que otros métodos. Es una cuestión de estilo (programación funcional versus programación procesal, pero no solo) considerarlo más complicado o no.
Nicolas Henneaux
3

Si tiene que trabajar con matrices en bruto y no ArrayListse Arraystiene lo que necesita. Si observa el código fuente, estas son las mejores formas de obtener una copia de una matriz. Tienen una buena programación defensiva porque el System.arraycopy()método arroja muchas excepciones no verificadas si lo alimenta con parámetros ilógicos.

Puede usar cualquiera de los dos Arrays.copyOf()que copiará desde el primer Nthelemento al nuevo conjunto más corto.

public static <T> T[] copyOf(T[] original, int newLength)

Copia la matriz especificada, truncando o rellenando con nulos (si es necesario) para que la copia tenga la longitud especificada. Para todos los índices que son válidos tanto en la matriz original como en la copia, las dos matrices contendrán valores idénticos. Para cualquier índice que sea válido en la copia pero no en el original, la copia contendrá nulo. Tales índices existirán si y solo si la longitud especificada es mayor que la de la matriz original. La matriz resultante es exactamente de la misma clase que la matriz original.

2770
2771    public static <T,U> T[] More ...copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
2772        T[] copy = ((Object)newType == (Object)Object[].class)
2773            ? (T[]) new Object[newLength]
2774            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
2775        System.arraycopy(original, 0, copy, 0,
2776                         Math.min(original.length, newLength));
2777        return copy;
2778    }

o Arrays.copyOfRange()también hará el truco:

public static <T> T[] copyOfRange(T[] original, int from, int to)

Copia el rango especificado de la matriz especificada en una nueva matriz. El índice inicial del rango (desde) debe estar entre cero y la longitud original, inclusive. El valor en original [from] se coloca en el elemento inicial de la copia (a menos que sea == original.length o from == to). Los valores de elementos posteriores en la matriz original se colocan en elementos posteriores en la copia. El índice final del rango (a), que debe ser mayor o igual que from, puede ser mayor que original.length, en cuyo caso se coloca nulo en todos los elementos de la copia cuyo índice es mayor o igual que el original. longitud - desde. La longitud de la matriz devuelta será desde -. La matriz resultante es exactamente de la misma clase que la matriz original.

3035    public static <T,U> T[] More ...copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType) {
3036        int newLength = to - from;
3037        if (newLength < 0)
3038            throw new IllegalArgumentException(from + " > " + to);
3039        T[] copy = ((Object)newType == (Object)Object[].class)
3040            ? (T[]) new Object[newLength]
3041            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
3042        System.arraycopy(original, from, copy, 0,
3043                         Math.min(original.length - from, newLength));
3044        return copy;
3045    }

Como puede ver, ambos son solo funciones de envoltura System.arraycopycon lógica defensiva de que lo que está tratando de hacer es válido.

System.arraycopy es la forma más rápida de copiar matrices.


fuente
0

Tuve un problema similar con las matrices 2D y terminé aquí. Estaba copiando la matriz principal y cambiando los valores de las matrices internas, y me sorprendió cuando los valores cambiaron en ambas copias. Básicamente, ambas copias eran independientes pero contenían referencias a las mismas matrices internas y tuve que hacer una matriz de copias de las matrices internas para obtener lo que quería.

Probablemente no sea el problema del OP, pero espero que pueda ser útil.

Harpistry
fuente