Come posso clonare un elenco generico in Java?


155

Ne ho uno di ArrayList<String>cui vorrei restituire una copia. ArrayListha un metodo clone che ha la seguente firma:

public Object clone()

Dopo aver chiamato questo metodo, come posso ricollegare l'oggetto restituito ArrayList<String>?


16
No, questa è una domanda valida. Java non supporta generici "veri", con la cancellazione del tipo di runtime e tutto il resto, quindi questi tipi di dettagli possono essere complicati. Inoltre, l'interfaccia Cloneable e il meccanismo del metodo Object.clone () sono altrettanto confusi.
Programmatore fuorilegge,

OK, faccio principalmente C # dove questo è davvero facile. Per favore fatemi sapere se volete che rimuova i commenti da questa domanda.
Espo,

1
Puoi lasciare il commento. Penso che le mie modifiche abbiano spiegato con cosa avevo problemi.
Bill the Lizard,

2
Il tuo commento è OK, se un po 'condiscendente. Immagino che molti cerchi attraverso i quali gli sviluppatori Java devono saltare sembrano sciocchi agli sviluppatori .NET.
Programmatore fuorilegge il

1
@Oscar, vuole clonare e non invocare il comando clone. Potrebbe non essere la stessa cosa che il clone non clona davvero. Penso che questo sia il punto. Questa è davvero una domanda difficile.
Rafa,

Risposte:


62
ArrayList newArrayList = (ArrayList) oldArrayList.clone();

43
Funzionerà perfettamente con le stringhe (che è ciò che la domanda è stata posta), ma vale la pena notare che ArrayList.clone eseguirà una copia superficiale, quindi se ci fossero oggetti mutabili nell'elenco, non verranno clonati (e cambiandone uno in un elenco cambierà anche quello dell'altro elenco.
pkaeding,

49
Dovresti evitare di usare i tipi non elaborati in tutto tranne che nel codice legacy. Stai meglio usando ArrayList<String> newArrayList = (ArrayList<String>) oldArrayList.clone();.
cdmckay,

20
È un peccato che ArrayList abbia un metodo #clone, ma List in sé no. Sospiro.
rogerdpack,

12
Non correlato, ma quando ci sei: usare List<String>invece ArrayList<String>sul lato sinistro. Questo è il modo in cui le raccolte dovrebbero essere utilizzate la maggior parte del tempo.
Christophe Roussy,

3
Non ho potuto usare questo metodo per duplicare un ArrayList. Il metodo clone () non è stato riconosciuto.
Jack,

318

Perché vorresti clonare? La creazione di un nuovo elenco di solito ha più senso.

List<String> strs;
...
List<String> newStrs = new ArrayList<>(strs);

Lavoro fatto.


17
Potresti non sapere che tipo di Elenco è. Potrebbe essere un oggetto LinkedList, MyOwnCustomList o una sottoclasse di ArrayList, nel qual caso la novità di un ArrayList sarebbe del tipo errato.
Steve Kuo,

51
Mi interesserebbe l'implementazione dell'elenco originale utilizzato? Probabilmente mi interessa quale implementazione usi il nuovo elenco.
Tom Hawtin - tackline

12
@Steve Kuo: la firma ArrayList(Collection<? extends E> c)significa che non importa quale tipo di elenco usi come argomento.
cdmckay,

3
Voglio dire, non è una copia profonda, vero?

4
@YekhezkelYovel No, non lo sarebbe.
Tom Hawtin - tackline

19

Questo è il codice che uso per questo:

ArrayList copy = new ArrayList (original.size());
Collections.copy(copy, original);

La speranza è utile per te


3
Evita di usare tipi grezzi .. quindi invece di ArrayListusareArrayList<YourObject>
milosmns il

2
docs.oracle.com/javase/8/docs/api/java/util/… Non funziona perché le dimensioni dell'elenco sono diverse.
MLProgrammer-CiM

Perché non dovresti semplicemente usare il sovraccarico di copia del ArrayListcostruttore?
Tom Hawtin - tackline il

19

Con Java 8 può essere clonato con un flusso.

import static java.util.stream.Collectors.toList;

...

List<AnObject> clone = myList.stream().collect(toList());

Può essere fatto come List <AnObject> xy = new ArrayList <> (oldList);
mirzak,

1
Questa non è una copia approfondita, i cambiamenti sugli elementi di una lista possono essere visti in un'altra
Inchara,

2
Dove nella domanda specifica che è richiesta una copia profonda? Il presupposto è che è necessaria un'altra raccolta contenente gli stessi oggetti. Se vuoi seguire il percorso della copia profonda, stai aprendo una nuova lattina di worm.
Simon Jenkins,

Non è proprio un clone, vero? Dai documenti API "Non ci sono garanzie sul tipo, la mutabilità, la serializzabilità o la sicurezza dei thread dell'elenco restituito". Euw, non ne voglio uno. toCollectionpotrebbe essere una scelta migliore.
Tom Hawtin - tackline il

16

Si noti che Object.clone () presenta alcuni problemi importanti e il suo utilizzo è sconsigliato nella maggior parte dei casi. Si prega di vedere l'articolo 11, da " Effective Java " di Joshua Bloch per una risposta completa. Credo che tu possa usare tranquillamente Object.clone () su array di tipi primitivi, ma a parte questo devi essere prudente sull'uso e l'override del clone. Probabilmente stai meglio definendo un costruttore di copie o un metodo factory statico che clona esplicitamente l'oggetto in base alla tua semantica.


15

Penso che questo dovrebbe fare il trucco usando l'API Collections:

Nota : il metodo di copia viene eseguito in tempo lineare.

//assume oldList exists and has data in it.
List<String> newList = new ArrayList<String>();
Collections.copy(newList, oldList);

13
Perché non usare semplicemente il nuovo ArrayList <String> (oldList)?
cdmckay,

4
Credo che non funzionerebbe, da doc * L'elenco delle destinazioni deve essere lungo almeno quanto l'elenco delle fonti. Se è più lungo, gli elementi rimanenti nell'elenco di destinazione non sono interessati.
Greg Domjan,

@GregDomjan non sono d'accordo sul fatto che questo è il modo migliore, ma è un modo per farlo. Per ovviare al problema è semplice come questo: Elenco <String> newList = new ArrayList <> (oldList.size ());
nckbrz,

1
Non sono sicuro che sia correlato a Java 8, ma anche quando si specifica la dimensione, si ottiene comunque un'eccezione IndexOutOfBoundsException: destination.size () <source.size (): 0 <2 su List<MySerializableObject> copyList = new ArrayList<>(mMySerializableObjects.size()); Sembra che l'utilizzo copyList.addAll(original);sia una buona alternativa
Gene Bo

@GeneBo Non specifica la dimensione. Sta specificando la capacità, che è una cosa molto diversa. La gioia degli intargomenti misteriosi . Non ho idea del perché tu voglia usare questo oscuro metodo statico invece di un buon vecchio addAll.
Tom Hawtin: affronta il

8

Trovo che usando addAll funzioni bene.

ArrayList<String> copy = new ArrayList<String>();
copy.addAll(original);

vengono utilizzate le parentesi anziché la sintassi generica


5
Funzionerà con Stringhe, ma non con oggetti mutabili. Ti consigliamo di clonare anche quelli.
jodonnell,

Sì, la sua domanda è per le stringhe. E ha il problema dei generici che in questo caso non amano molto le cose del casting.
Allain Lalonde,

Inoltre, ArrayList.clone eseguirà solo un clone superficiale, quindi anche gli oggetti mutabili nell'elenco non verranno clonati utilizzando quel metodo.
pkaeding,

Dovrebbe essere ArrayList <String> tra l'altro. Inoltre, probabilmente stai meglio usando il nuovo ArrayList <String> (originale) in quanto è meno scritto e altrettanto chiaro.
cdmckay,

4
Perché non usare solo new ArrayList<String>(original)?
user102008

6

Se vuoi questo per essere in grado di restituire l'elenco in un getter, sarebbe meglio fare:

ImmutableList.copyOf(list);

4

Per clonare un'interfaccia generica come java.util.Listte dovrai solo lanciarla. ecco un esempio:

List list = new ArrayList();
List list2 = ((List) ( (ArrayList) list).clone());

È un po 'complicato, ma funziona, se sei limitato a restituire Listun'interfaccia, quindi chiunque dopo puoi implementare la tua lista quando vuole.

So che questa risposta è vicina alla risposta finale, ma la mia risposta risponde come fare tutto ciò mentre lavori con Listil genitore genericoArrayList


2
stai assumendo che sarà sempre un ArrayList che non è vero
jucardi

@JuanCarlosDiaz lo farà, questa è la domanda posta, si trattava di ArrayList, quindi ho risposto per ArrayList :)
Ahmed Hamdy,

2

Prestare molta attenzione durante la clonazione di ArrayLists. La clonazione in Java è superficiale. Ciò significa che clonerà solo l'Arraylist stesso e non i suoi membri. Quindi, se si dispone di un ArrayList X1 e lo si clona in X2, qualsiasi cambiamento in X2 si manifesterà anche in X1 e viceversa. Quando clonerai genererai solo una nuova ArrayList con puntatori agli stessi elementi nell'originale.


2

Questo dovrebbe funzionare anche:

ArrayList<String> orig = new ArrayList<String>();
ArrayList<String> copy = (ArrayList<String>) orig.clone()

2
List<String> shallowClonedList = new ArrayList<>(listOfStrings);

Tieni presente che questa è solo una copia superficiale, non una copia profonda, vale a dire. si ottiene un nuovo elenco, ma le voci sono le stesse. Questo non è un problema per semplici stringhe. Diventa più complicato quando le voci dell'elenco sono oggetti stessi.


1
ArrayList first = new ArrayList ();
ArrayList copy = (ArrayList) first.clone ();

1

Non sono un professionista di Java, ma ho lo stesso problema e ho provato a risolvere con questo metodo. (Suppone che T abbia un costruttore di copie).

 public static <T extends Object> List<T> clone(List<T> list) {
      try {
           List<T> c = list.getClass().newInstance();
           for(T t: list) {
             T copy = (T) t.getClass().getDeclaredConstructor(t.getclass()).newInstance(t);
             c.add(copy);
           }
           return c;
      } catch(Exception e) {
           throw new RuntimeException("List cloning unsupported",e);
      }
}

Non c'è garanzia che la Listclasse di implementazione abbia un costruttore pubblico no-arg. Ad esempio, la Lists ritorna da Array.asList, List.ofo List.subListprobabilmente no.
Tom Hawtin - tackline il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.