Cópia Java ArrayList

214

Eu tenho um ArrayList l1tamanho 10. Atribuo l1ao novo tipo de referência de lista l2. Will l1e l2apontar para o mesmo ArrayListobjeto? Ou é uma cópia do ArrayListobjeto atribuída l2?

Ao usar a l2referência, se eu atualizar o objeto de lista, ele também refletirá as alterações no l1tipo de referência.

Por exemplo:

List<Integer> l1 = new ArrayList<Integer>();
for (int i = 1; i <= 10; i++) {
    l1.add(i);
}

List l2 = l1;
l2.clear();

Não há outra maneira de atribuir uma cópia de um objeto de lista a uma nova variável de referência, além de criar 2 objetos de lista e fazer cópias em coleções de antigas para novas?

user309281
fonte

Respostas:

460

Sim, a tarefa apenas copiará o valor de l1(que é uma referência) para l2. Ambos se referirão ao mesmo objeto.

Criar uma cópia superficial é bastante fácil:

List<Integer> newList = new ArrayList<>(oldList);

(Apenas como um exemplo.)

Jon Skeet
fonte
1
É possível copiar apenas uma parte de uma lista de matrizes para uma nova lista de matrizes, com eficiência. por exemplo: copie elementos entre as posições 5 e 10 de uma lista de matriz para outra nova lista de matriz. Na minha aplicação, o alcance seria muito maior.
Ashwin
1
@ Ashwin: Bem, é uma operação O (N), mas sim ... você pode usar List.subListpara obter uma "visualização" em uma seção da lista original.
Jon Skeet
e se as listas de matrizes estiverem aninhadas (ArrayList<ArrayList<Object>>)? isso criaria recursivamente cópias de todos os objetos filhos ArrayList?
Cat
3
@ Cat: Não ... Esta é apenas uma cópia superficial.
Jon Skeet
1
@ ShanikaEdiriweera: Você poderia fazê-lo de maneira fluente com fluxos, sim. Mas a parte complicada é criar uma cópia profunda, que a maioria dos objetos não fornece. Se você tem um caso específico em mente, sugiro que faça uma nova pergunta com detalhes.
Jon Skeet
67

Tente usar Collections.copy(destination, source);

Sergii Zagriichuk
fonte
14
Gostaria de explicar por que isso pode ser preferível new ArrayList<>(source);?
Alex
6
@atc é mais uma maneira de fazer cópia superficial, em vez de new ArrayList () é utilizado outro algoritmo e pode ser usado para qualquer implementação de lista, não apenas para um ArrayList, isso é tudo :)
Sergii Zagriichuk
14
Este método é muito enganador! Na verdade, é descrição é. Ele diz: "copia elementos de uma lista de fontes para o destino", mas eles não são copiados! Eles são referenciados por isso haverá apenas uma cópia dos objetos e se eles são mutáveis, você está em apuros
ACV
5
nada em java-api clonagem profunda é feito por qualquer classe de coleção
Vikash
Essa resposta não faz muito sentido. Collections.copynão é de todo uma alternativa para new ArrayList<>(source). O que Collections.copyrealmente faz é supor que destination.size()seja pelo menos tão grande quanto source.size()e, em seguida, copie o intervalo índice por índice usando o set(int,E)método O método não adiciona novos elementos ao destino. Consulte o código-fonte se não estiver claro o suficiente no Javadoc.
Radiodef 03/08/19
35

Sim l1e l2apontará para a mesma referência, o mesmo objeto.

Se você deseja criar um novo ArrayList com base no outro ArrayList, faça isso:

List<String> l1 = new ArrayList<String>();
l1.add("Hello");
l1.add("World");
List<String> l2 = new ArrayList<String>(l1); //A new arrayList.
l2.add("Everybody");

O resultado será l1ainda terá 2 elementos e l2terá 3 elementos.

Alfredo Osorio
fonte
Você pode explicar a diferença entre List<String> l2 = new ArrayList<String>(l1)e List<String> l2 = l1?
MortalMan
@MortalMan, a diferença é que l2 = new ArrayList <> (l1) é um objeto totalmente novo e a modificação de l2 não afeta l1, enquanto Lista <String> l2 = l1 você não está criando um novo objeto, mas apenas referenciando o mesmo objeto como l1; nesse caso, fazendo uma operação como l2.add ("Todos"), l1.size () e l2.size () retornará 3 porque ambos estão fazendo referência ao mesmo objeto.
Alfredo Osorio
19

Outra maneira conveniente de copiar os valores de src ArrayList para dest Arraylist é a seguinte:

ArrayList<String> src = new ArrayList<String>();
src.add("test string1");
src.add("test string2");
ArrayList<String> dest= new ArrayList<String>();
dest.addAll(src);

Isso é cópia de valores real e não apenas cópia de referência.

Harshal Waghmare
fonte
10
não tenho certeza absoluta de que isso seja preciso. meu teste mostra o oposto (ainda referenciar mesmo objecto)
Invertigo
esta solução funcionou para mim quando se usa ArrayList com ArrayAdapter
albanx
1
Esta resposta está errada. addAll () apenas copia as referências como invertigo disse. Esta não é uma cópia profunda.
Jk7 28/0218
Para um ArrayList <>, essa resposta é aceitável porque String é imutável, mas tente com o exemplo do OP, um ArraList <Integer>, e você verá que está apenas copiando referências.
Jk7 28/02
Apenas não é o meu dia, eu acho. Acontece que classes como Integer e Long também são imutáveis, portanto a resposta de Harshal funciona em casos simples, como ArrayList <Integer> e ArrayList <>. Onde falha é para objetos complexos que não são imutáveis.
Jk7 28/02
8

Existe um método addAll () que servirá ao propósito de copiar um ArrayList para outro.

Por exemplo, você tem duas listas de matrizes : sourceList e targetList , use o código abaixo.

targetList.addAll (sourceList);

vaibhav agrawal
fonte
também apenas copia referências.
Vikash
4

Java não passa objetos, ele passa referências (ponteiros) para objetos. Então sim, l2 e l1 são dois ponteiros para o mesmo objeto.

Você precisa fazer uma cópia explícita se precisar de duas listas diferentes com o mesmo conteúdo.

JB Nizet
fonte
3
Como você faz "uma cópia explícita"? Suponho que você esteja falando de uma cópia profunda?
Cin316
1

List.copyOf List lista não modificável

Você perguntou:

Não há outra maneira de atribuir uma cópia de uma lista

O Java 9 trouxe os List.ofmétodos para o uso de literais para criar uma Listclasse concreta não modificável e desconhecida.

LocalDate today = LocalDate.now( ZoneId.of( "Africa/Tunis" ) ) ;
List< LocalDate > dates = List.of( 
    today.minusDays( 1 ) ,  // Yesterday
    today ,                 // Today
    today.plusDays( 1 )     // Tomorrow
);

Junto com isso, também conseguimos List.copyOf. Esse método também retorna uma Listclasse concreta não modificável e desconhecida.

List< String > colors = new ArrayList<>( 4 ) ;          // Creates a modifiable `List`. 
colors.add ( "AliceBlue" ) ;
colors.add ( "PapayaWhip" ) ;
colors.add ( "Chartreuse" ) ;
colors.add ( "DarkSlateGray" ) ;
List< String > masterColors = List.copyOf( colors ) ;   // Creates an unmodifiable `List`.

Por "não modificável", queremos dizer que o número de elementos na lista e o referente referente ao objeto mantido em cada slot como um elemento são fixos. Você não pode adicionar, soltar ou substituir elementos. Mas o objeto referente mantido em cada elemento pode ou não ser mutável .

colors.remove( 2 ) ;          // SUCCEEDS. 
masterColors.remove( 2 ) ;    // FAIL - ERROR.

Veja este código executado ao vivo em IdeOne.com .

datas.toString (): [2020-02-02, 2020-02-03, 2020-02-04]

colors.toString (): [AliceBlue, PapayaWhip, DarkSlateGray]

masterColors.toString (): [AliceBlue, PapayaWhip, Chartreuse, DarkSlateGray]

Você perguntou sobre referências a objetos. Como outros disseram, se você criar uma lista e atribuí-la a duas variáveis ​​de referência (ponteiros), ainda terá apenas uma lista. Ambos apontam para a mesma lista. Se você usar um ponteiro para modificar a lista, os dois ponteiros verão as alterações posteriormente, pois há apenas uma lista na memória.

Então você precisa fazer uma cópia da lista. Se você deseja que a cópia não seja modificável, use o List.copyOfmétodo conforme discutido nesta resposta. Nesta abordagem, você acaba com duas listas separadas, cada uma com elementos que mantêm uma referência aos mesmos objetos de conteúdo. Por exemplo, em nosso exemplo acima, usando Stringobjetos para representar cores, os objetos de cores estão flutuando na memória em algum lugar. As duas listas contêm ponteiros para os mesmos objetos de cor. Aqui está um diagrama.

insira a descrição da imagem aqui

A primeira lista colorsé modificável. Isso significa que alguns elementos podem ser removidos como visto no código acima, onde removemos o terceiro elemento original Chartreuse(índice de 2 = ordinal 3). E elementos podem ser adicionados. E os elementos podem ser alterados para apontar para outros String, como OliveDrabou CornflowerBlue.

Por outro lado, os quatro elementos de masterColorssão fixos. Sem remoção, sem adição e sem substituição de outra cor. Essa Listimplementação não é modificável.

Basil Bourque
fonte