Faça cópia de uma matriz


345

Eu tenho uma matriz aque está sendo constantemente atualizada. Vamos dizer a = [1,2,3,4,5]. Preciso fazer uma cópia exata exata ae chamá-lo b. Se afosse mudar para [6,7,8,9,10], bainda deveria ser [1,2,3,4,5]. Qual é a melhor maneira de fazer isso? Eu tentei um forloop como:

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

mas isso não parece funcionar corretamente. Por favor, não use termos avançados como cópia em profundidade, etc., porque não sei o que isso significa.

Respostas:


558

Você pode tentar 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 );

Mas, provavelmente é melhor usar clone () na maioria dos casos:

int[] src = ...
int[] dest = src.clone();

9
+1 por não reiventar a roda. E até onde eu sei, essa solução é a mais rápida que você pode obter na cópia de matriz.
Felipe Felipe # 26/11

6
o clone e a arraycopy são nativos. Eu esperaria que o clone fosse marginalmente mais rápido. não que a diferença importe.
precisa saber é o seguinte

5
@Felipe, @MeBigFatGuy - apenas para uma grande variedade. Para uma matriz pequena, um loop de cópia pode ser mais rápido devido às despesas gerais da instalação. Se você olhar para o javadoc System.arraycopy, verá que o método precisa verificar várias coisas antes de iniciar. Algumas dessas verificações são desnecessárias com um loop de cópia, dependendo dos tipos de matriz estática.
Stephen C

7
@FelipeHummel, @MeBigFatGuy, @StephenC - Aqui está um teste de desempenho dos métodos de cópia de matriz mencionados nas respostas aqui. Nessa configuração, clone()acaba sendo o mais rápido para 250.000 elementos.
Adam

6
É decepcionante ver que toda a discussão aqui é sobre problemas de micro desempenho, que 99,999% do tempo, não importam. O ponto mais importante é que src.clone()é mais legível e tem muito menos oportunidade de erro do que alocar uma nova matriz e executar arraycopy. (E também é rápido).
Brian Goetz

231

você pode usar

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

também.


6
Estou apenas esclarecendo o argumento do OP: " Se A mudasse para [6,7,8,9,10], B ainda deveria ser [1,2,3,4,5] ". OP disse que tentou usar loop, mas não funcionou para ele.
Harry Joy

15
O elenco é desnecessário; um bom analisador estático avisa sobre isso. Mas a clonagem é definitivamente a melhor maneira de fazer uma nova cópia de uma matriz.
Erickson

5
@MeBigFatGuy - o caso de uso do OP implica cópias repetidas na mesma matriz, para que o clone não funcione.
Stephen C

4
@ Stephen C, eu não li isso. Acabei de ler que ele quer uma cópia e, em seguida, atualizará repetidamente a versão não escondida.
precisa saber é o seguinte

4
@MeBigFatGuy - ele disse: "Eu tenho uma matriz A que está sendo atualizada constantemente". . Talvez eu esteja lendo muito sobre isso, mas entendo isso como se ele estivesse repetidamente copiando A para B também.
Stephen C

184

Se você deseja fazer uma cópia de:

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

Este é o caminho a seguir:

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

Arrays.copyOfpode ser mais rápido do que a.clone()em pequenas matrizes. Ambos os elementos de cópia são igualmente rápidos, mas clone () retorna Objectpara que o compilador precise inserir uma conversão implícita em int[]. Você pode vê-lo no bytecode, algo como isto:

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

62

Boa explicação de http://www.journaldev.com/753/how-to-copy-arrays-in-java

Métodos de cópia de matriz Java

Object.clone () : a classe Object fornece o método clone () e, como o array em java também é um Object, você pode usar esse método para obter uma cópia completa do array. Este método não combina com você, se você deseja uma cópia parcial da matriz.

System.arraycopy () : a classe System system arraycopy () é a melhor maneira de fazer uma cópia parcial de uma matriz. Ele fornece uma maneira fácil de especificar o número total de elementos a serem copiados e as posições de índice da matriz de origem e destino. Por exemplo, System.arraycopy (origem, 3, destino, 2, 5) copiará 5 elementos da origem para o destino, começando do 3º índice da origem ao 2º índice do destino.

Arrays.copyOf (): se você deseja copiar os primeiros elementos de uma matriz ou a cópia completa da matriz, é possível usar esse método. Obviamente, não é versátil como System.arraycopy (), mas também não é confuso e fácil de usar.

Arrays.copyOfRange () : se você quiser que alguns elementos de uma matriz sejam copiados, em que o índice inicial não seja 0, use esse método para copiar a matriz parcial.


35

Sinto que todas essas "melhores maneiras de copiar uma matriz" não vão realmente resolver o seu problema.

Você diz

Eu tentei um loop for como [...] mas isso não parece estar funcionando corretamente?

Olhando para esse loop, não há razão óbvia para que ele não funcione ... a menos que:

  • de alguma forma, você tem as matrizes ae bbagunçadas (por exemplo, ae se brefere à mesma matriz) ou
  • seu aplicativo é multiencadeado e diferentes encadeamentos estão lendo e atualizando a amatriz simultaneamente.

Em ambos os casos, maneiras alternativas de fazer a cópia não resolverão o problema subjacente.

A correção para o primeiro cenário é óbvia. Para o segundo cenário, você precisará descobrir uma maneira de sincronizar os threads. As classes de matriz atômica não ajudam porque não possuem construtores de cópia atômica ou métodos de clone, mas a sincronização usando um mutex primitivo fará o truque.

(Há dicas na sua pergunta que me levam a pensar que isso está realmente relacionado ao segmento; por exemplo, sua declaração que aestá constantemente mudando.)


2
concordou .. provavelmente verdade.
precisa saber é o seguinte


9

Toda solução que chama comprimento da matriz, adicione seu código redundante nulo checkersconsider exemplo:

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

Eu recomendo que você não invente a roda e use a classe de utilidade onde todas as verificações necessárias já foram realizadas. Considere ArrayUtils do apache commons. Seu código se torna mais curto:

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

Apache commons você pode encontrar


8

Você também pode usar Arrays.copyOfRange.

Exemplo :

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 é semelhante Arrays.copyOf, mas é mais flexível. Ambos usam System.arraycopysob o capô.

Veja :


3

Para uma cópia nula segura de uma matriz, você também pode usar um opcional com o Object.clone()método fornecido nesta resposta .

int[] arrayToCopy = {1, 2, 3};
int[] copiedArray = Optional.ofNullable(arrayToCopy).map(int[]::clone).orElse(null);

Apesar de a solução estar muito complicada, ela também introduz desperdício de memória e, se a matriz contém um segredo (por exemplo, matriz de bytes com senha), também apresenta falhas de segurança porque os objetos intermediários permanecerão na pilha até que a coleta de lixo possa ser exposta. para atacantes.
Weltraumschaf

11
Não concordo que a matriz esteja na pilha especialmente para essa construção. Na verdade, ele chama clone apenas quando necessário e o Optionalobjeto é apenas um objeto vazio com uma referência à matriz existente. Sobre o impacto no desempenho, eu diria que é prematuro dizer que é realmente um impacto, já que esse tipo de construção é um bom candidato para incluir na JVM e, em seguida, não tem mais impacto que outros métodos. É uma questão de estilo (programação funcional versus programação processual, mas não apenas) considerá-la mais complicada ou não.
Nicolas Henneaux 16/05/19

3

Se você precisa trabalhar com matrizes de matérias e não ArrayListem seguida, Arraystem o que você precisa. Se você olhar para o código-fonte, essas são as melhores maneiras de obter uma cópia de uma matriz. Eles têm uma boa parte de programação defensiva porque o System.arraycopy()método lança muitas exceções não verificadas se você alimentar parâmetros ilógicos.

Você pode usar o Arrays.copyOf()que copiará do primeiro para o Nthelemento para a nova matriz mais curta.

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

Copia a matriz especificada, truncando ou preenchendo com nulos (se necessário) para que a cópia tenha o comprimento especificado. Para todos os índices válidos na matriz original e na cópia, as duas matrizes conterão valores idênticos. Para quaisquer índices válidos na cópia, mas não no original, a cópia conterá nulo. Esses índices existirão se, e somente se, o comprimento especificado for maior que o da matriz original. A matriz resultante é exatamente da mesma classe que a 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    }

ou Arrays.copyOfRange()também fará o truque:

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

Copia o intervalo especificado da matriz especificada para uma nova matriz. O índice inicial do intervalo (de) deve estar entre zero e comprimento original, inclusive. O valor no original [from] é colocado no elemento inicial da cópia (a menos que = = original.length ou de == a). Os valores dos elementos subseqüentes na matriz original são colocados nos elementos subseqüentes na cópia. O índice final do intervalo (a), que deve ser maior ou igual a partir de, pode ser maior que original.length, caso em que nulo é colocado em todos os elementos da cópia cujo índice é maior ou igual ao original. comprimento - de. O comprimento da matriz retornada será de - para. A matriz resultante é exatamente da mesma classe que a 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 você pode ver, ambas são apenas funções de invólucro System.arraycopycom lógica defensiva de que o que você está tentando fazer é válido.

System.arraycopy é a maneira mais rápida de copiar matrizes.


0

Eu tive um problema semelhante com matrizes 2D e terminei aqui. Eu estava copiando a matriz principal e alterando os valores das matrizes internas e fiquei surpreso quando os valores mudaram nas duas cópias. Basicamente, ambas as cópias eram independentes, mas continham referências às mesmas matrizes internas e eu tive que fazer uma série de cópias das matrizes internas para conseguir o que queria.

Provavelmente não é problema do OP, mas espero que ainda possa ser útil.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.