Jak sklonować listę ogólną w języku Java?


155

Mam ArrayList<String>kopię, którą chciałbym zwrócić. ArrayListma metodę klonowania, która ma następujący podpis:

public Object clone()

Po wywołaniu tej metody, jak mam rzutować zwrócony Object z powrotem do ArrayList<String>?


16
Nie, to ważne pytanie. Java nie obsługuje „prawdziwych” typów ogólnych, z wymazywaniem typów w czasie wykonywania i wszystkimi innymi, więc tego rodzaju szczegóły mogą być trudne. Ponadto interfejs Cloneable i mechanizm metody Object.clone () są podobnie zagmatwane.
Outlaw Programmer

OK, głównie robię C #, gdzie jest to naprawdę łatwe. Daj mi znać, jeśli chcesz, abym usunął komentarze z tego pytania.
Espo

1
Możesz zostawić komentarz. Myślę, że moje zmiany wyjaśniły, z czym mam problem.
Bill the Lizard

2
Twój komentarz jest w porządku, choć trochę protekcjonalny. Wyobrażam sobie, że wiele przeszkód, przez które muszą przejść programiści Java, wydaje się głupim programistom .NET.
Outlaw Programmer

1
@Oscar, chce klonować, a nie wywoływać polecenia clone. Może to nie być to samo, co klon tak naprawdę nie klonuje. Myślę, że o to chodzi. To naprawdę trudne pytanie.
Rafa

Odpowiedzi:


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

43
To zadziała dobrze w przypadku Strings (o co chodzi w pytaniu), ale warto zauważyć, że ArrayList.clone wykona płytką kopię, więc jeśli na liście znajdują się zmienne obiekty, nie zostaną one sklonowane (i zmieni jeden w jednej liście nie zmieni, że jedna na drugiej liście, jak również.
pkaeding

49
Należy unikać używania typów surowych w żadnym innym kodzie niż starszym. Lepiej użyj ArrayList<String> newArrayList = (ArrayList<String>) oldArrayList.clone();.
cdmckay

20
Szkoda, że ​​ArrayList ma metodę #clone, ale sama List nie. Westchnienie.
rogerdpack

12
Nie powiązane, ale w tym momencie: użyj List<String>zamiast ArrayList<String>po lewej stronie. W ten sposób kolekcje powinny być używane przez większość czasu.
Christophe Roussy

3
Nie mogłem użyć tej metody do zduplikowania ArrayList. Nie rozpoznano metody clone ().
Jack

318

Dlaczego chcesz klonować? Tworzenie nowej listy ma zwykle większy sens.

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

Zadanie wykonane.


17
Możesz nie wiedzieć, jaki to typ listy. Może to być LinkedList, MyOwnCustomList lub podklasa ArrayList, w którym to przypadku nowe ArrayList byłoby niepoprawnym typem.
Steve Kuo

51
Czy obchodzi mnie, której implementacji użyła oryginalna lista? Prawdopodobnie obchodzi mnie, której implementacji używa nowa lista.
Tom Hawtin - tackline

12
@Steve Kuo: Podpis ArrayList(Collection<? extends E> c)oznacza, że ​​nie ma znaczenia, jakiego rodzaju listy użyjesz jako argumentu.
cdmckay

3
To znaczy, to nie jest głęboka kopia, prawda?

4
@YekhezkelYovel Nie, nie byłoby.
Tom Hawtin - tackline

19

Oto kod, którego używam do tego:

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

Nadzieja jest dla ciebie przydatna


3
I unikaj używania surowych typów ... więc zamiast ArrayListużywaćArrayList<YourObject>
milosmns

2
docs.oracle.com/javase/8/docs/api/java/util/… Nie działa, ponieważ rozmiary list są różne.
MLProgrammer-CiM

Dlaczego nie miałbyś po prostu użyć przeciążenia kopiowania pliku ArrayList konstruktora?
Tom Hawtin - tackline

19

W Javie 8 można go sklonować za pomocą strumienia.

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

...

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

Można to zrobić tak jak List <AnObject> xy = new ArrayList <> (oldList);
mirzak

1
To nie jest głęboka kopia, zmiany w elementach jednej listy można zobaczyć w innej
Inchara

2
Gdzie w pytaniu określono, że wymagana jest głęboka kopia? Założenie jest takie, że wymagana jest inna kolekcja zawierająca te same obiekty. Jeśli chcesz zejść z głębokiej ścieżki kopiowania, otwierasz zupełnie nową puszkę robaków.
Simon Jenkins,

Nie jest to jednak klon, prawda? Z dokumentacji API „Nie ma gwarancji co do typu, zmienności, możliwości serializacji lub bezpieczeństwa wątków zwróconej listy”. Euw, nie chcę jednego z nich. toCollectionmoże być lepszym wyborem.
Tom Hawtin - tackline

16

Należy pamiętać, że Object.clone () ma poważne problemy i odradza się jego używanie w większości przypadków. Pełną odpowiedź można znaleźć w punkcie 11 książkiEfektywna Java ” Joshuy Blocha. Uważam, że możesz bezpiecznie używać Object.clone () na tablicach typów prymitywnych, ale poza tym musisz rozsądnie używać i nadpisywać clone. Prawdopodobnie lepiej będzie zdefiniować konstruktor kopiujący lub statyczną metodę fabryki, która jawnie klonuje obiekt zgodnie z twoją semantyką.


15

Myślę, że to powinno załatwić sprawę przy użyciu Collection API:

Uwaga : metoda kopiowania działa w czasie liniowym.

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

13
Dlaczego po prostu nie użyć new ArrayList <String> (oldList)?
cdmckay

4
Wierzę, że to by nie zadziałało, z doc * Lista docelowa musi być przynajmniej tak długa, jak lista źródeł. Jeśli jest dłuższy, pozostałe elementy listy docelowej pozostają niezmienione.
Greg Domjan

@GregDomjan nie to, że zgadzam się, że to najlepszy sposób, ale jest to sposób na zrobienie tego. Aby obejść swój problem, jest to tak proste, jak to: List <String> newList = new ArrayList <> (oldList.size ());
nckbrz

1
Nie jestem pewien, czy jest to związane z Javą 8, ale nawet przy określaniu rozmiaru nadal pojawia się wyjątek IndexOutOfBoundsException: destination.size () <source.size (): 0 <2 on List<MySerializableObject> copyList = new ArrayList<>(mMySerializableObjects.size()); Wydaje się, że używanie copyList.addAll(original);jest dobrą alternatywą
Gene Bo

@GeneBo Nie określa rozmiaru. Określa pojemność, a to zupełnie inna sprawa. Radość z tajemniczych intargumentów. Nie mam pojęcia, dlaczego chcesz użyć tej mało znanej metody statycznej zamiast starej, dobrej addAll.
Tom Hawtin - linka

8

Uważam, że użycie addAll działa dobrze.

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

zamiast generycznej składni używane są nawiasy


5
To zadziała w przypadku ciągów znaków, ale nie w przypadku obiektów modyfikowalnych. Te również będziesz chciał sklonować.
jodonnell

Tak, jego pytanie dotyczy strun. I ma problem z generykami, które tak naprawdę nie lubią rzeczy odlewniczych w tym przypadku.
Allain Lalonde,

Ponadto ArrayList.clone wykona tylko płytkie klonowanie, więc zmienne obiekty na liście również nie zostaną sklonowane przy użyciu tej metody.
rozpoczęcie

Powinno to być ArrayList <String> btw. Ponadto prawdopodobnie lepiej będzie, jeśli użyjesz nowej ArrayList <String> (oryginalna), ponieważ jest mniej pisana i równie przejrzysta.
cdmckay

4
Dlaczego po prostu nie użyć new ArrayList<String>(original)?
user102008

6

Jeśli chcesz tego, aby móc zwrócić Listę w getterze, lepiej byłoby zrobić:

ImmutableList.copyOf(list);

4

Aby sklonować ogólny interfejs, taki jak java.util.Listty, wystarczy go przesłać. oto przykład:

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

Jest to trochę trudne, ale działa, jeśli jesteś ograniczony do zwrócenia pliku List interfejsu, więc każdy może zaimplementować twoją listę, kiedy tylko zechce.

Wiem, że ta odpowiedź jest bliska ostatecznej odpowiedzi, ale moja odpowiedź odpowiada, jak to wszystko zrobić, gdy pracujesz z List- generycznym rodzicem - nieArrayList



@JuanCarlosDiaz będzie, to jest zadawane pytanie było o ArrayList, więc odpowiedziałem to dla ArrayList :)
Ahmed hamdy

2

Zachowaj ostrożność podczas klonowania ArrayLists. Klonowanie w Javie jest płytkie. Oznacza to, że sklonuje tylko samego Arraylist, a nie jego członków. Więc jeśli masz ArrayList X1 i sklonujesz ją do X2, każda zmiana w X2 będzie również widoczna w X1 i odwrotnie. Podczas klonowania wygenerujesz tylko nową ArrayList ze wskaźnikami do tych samych elementów w oryginale.


2

Powinno to również działać:

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

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

Należy pamiętać, że jest to tylko płytka, a nie głęboka kopia, tj. otrzymasz nową listę, ale wpisy są takie same. Nie stanowi to problemu dla prostych strun. Get jest trudniejszy, gdy pozycje listy same w sobie są obiektami.


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

1

Nie jestem specjalistą od Java, ale mam ten sam problem i próbowałem rozwiązać tą metodą. (Zakłada się, że T ma konstruktora kopiującego).

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

Nie ma żadnej gwarancji, że Listklasa implementacji ma publiczny konstruktor bez argumentów. Na przykład, Lists powroty przez Array.asList, List.oflub List.subListprawdopodobnie nie.
Tom Hawtin - tackline
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.