Як клонувати загальний список на Java?


155

У мене є ArrayList<String>те, що я хотів би повернути копію. ArrayListмає метод клонування, який має такий підпис:

public Object clone()

Як я зателефоную цьому методу, як я можу повернути повернутий Об'єкт назад ArrayList<String>?


16
Ні, це справедливе питання. Java не підтримує "справжню" загальну версію, зі стиранням типу виконання та ін., Тому такі деталі можуть бути складними. Крім того, інтерфейс Cloneable та механізм методу Object.clone () аналогічно заплутані.
Програматор поза законом

Гаразд, я здебільшого роблю C #, де це дуже просто. Будь ласка, дайте мені знати, чи хочете я видалити коментарі з цього питання.
Еспо

1
Ви можете залишити коментар. Я думаю, що мої зміни пояснили, з чим я маю проблеми.
Білл Ящірка

2
Ваш коментар в порядку, якщо трохи поблажливий. Я думаю, що безліч обручів, через які розробники Java повинні проскочити, здається дурним розробникам .NET.
Програміст поза законом

1
@Oscar, він хоче клонуватись, а не викликати команду clone. Це може бути не те саме, що клон насправді не клонується. Я думаю, в цьому справа. Це справді складне питання.
Рафа

Відповіді:


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

43
Це буде добре працювати для Strings (про це і було задано питання), але варто відзначити, що ArrayList.clone виконає неглибоку копію, тому, якщо в списку були змінні об’єкти, вони не будуть клоновані (і змінюючи один в одному списку зміниться і той, в іншому списку
pkaeding

49
Вам слід уникати використання сировинних типів ні в чому, окрім застарілого коду. Вам краще використовувати ArrayList<String> newArrayList = (ArrayList<String>) oldArrayList.clone();.
cdmckay

20
Дуже погано ArrayList має метод #clone, але сам список цього не робить. Зітхнути.
rogerdpack

12
Не пов’язано, але при цьому: використовуйте List<String>замість ArrayList<String>лівого боку. Ось як колекції слід використовувати більшу частину часу.
Крістоф Руссі

3
Я не міг використовувати цей метод для копіювання ArrayList. Метод clone () не було розпізнано.
Джек

318

Чому б ви хотіли клонувати? Створення нового списку зазвичай має більше сенсу.

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

Робота виконана.


17
Ви можете не знати, що це за Список. Це може бути LinkedList, MyOwnCustomList або підклас ArrayList, в цьому випадку нове створення ArrayList було б неправильним типом.
Стів Куо

51
Мені було б байдуже, яка реалізація використовувала оригінальний список? Мені, певно, байдуже, яку реалізацію використовує новий список.
Том Хотін - тайклін

12
@Steve Kuo: Підпис ArrayList(Collection<? extends E> c)означає, що не має значення, який список ви використовуєте в якості аргументу.
cdmckay

3
Я маю на увазі, що це не глибока копія, чи не так?

4
@YekhezkelYovel Ні, не було б.
Том Хотін - тайклін

19

Це код, який я використовую для цього:

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

Надія корисна для вас


3
І уникайте використання сировинних типів .. тому замість ArrayListвикористанняArrayList<YourObject>
milosmns

2
docs.oracle.com/javase/8/docs/api/java/util/… Не працює, оскільки розміри списку різні.
MLProgrammer-CiM

Чому б вам просто не використовувати копію ArrayListконструктора?
Том Хотін - тайклін

19

З Java 8 його можна клонувати потоком.

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

...

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

Можна виконати як List <AnObject> xy = new ArrayList <> (oldList);
мірзак

1
Це не глибока копія, зміни в елементах одного списку можна побачити в іншому
Inchara

2
Де у питанні вказано, чи потрібна глибока копія? Припущення полягає в тому, що потрібна інша колекція, що містить ті самі об'єкти. Якщо ви хочете пройти шлях глибокої копії, тоді ви відкриваєте цілу нову банку глистів.
Саймон Дженкінс

Але насправді не клон, чи не так? З Документів API "Жодних гарантій щодо типу, змінності, серіалізаційності чи безпечності потоку поверненого списку не повернуто". Ев, не хочеш одного з таких. toCollectionможе бути кращим вибором.
Том Хотін - тайклін

16

Зауважте, що у Object.clone () є деякі основні проблеми, і його використання в більшості випадків перешкоджає. Щоб отримати повну відповідь, див. Пункт 11, " Ефективна Java " Джошуа Блоха. Я вважаю, що ви можете безпечно використовувати Object.clone () на масивах примітивних типів, але крім цього вам потрібно бути розумним щодо правильного використання та перекриття клону. Вам, мабуть, краще визначити конструктор копій або статичний заводський метод, який явно клонує об’єкт відповідно до вашої семантики.


15

Я думаю, що для цього слід зробити фокус за допомогою API Collections:

Примітка : метод копіювання працює в лінійний час.

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

13
Чому б просто не використовувати новий ArrayList <String> (oldList)?
cdmckay

4
Я вважаю, що це не спрацює, від doc * Список призначення повинен бути принаймні таким, як список джерел. Якщо вона довша, інші елементи в списку призначення не впливають.
Грег Дом’ян

@GregDomjan не те, що я згоден, що це найкращий спосіб, але це спосіб це зробити. Щоб обійти проблему, це так само симплеру: Список <String> newList = новий ArrayList <> (oldList.size ());
nckbrz

1
Не впевнений, що це пов’язано з Java 8, але навіть при вказівці розміру все ще отримує виняток IndexOutOfBoundsException: target.size () <source.size (): 0 <2 на List<MySerializableObject> copyList = new ArrayList<>(mMySerializableObjects.size()); Здається, використання copyList.addAll(original);хорошої альтернативи
Gene Bo

@GeneBo Не визначає розмір. Це уточнення ємності, що зовсім інша річ. Радість таємничих intаргументів. Я не знаю, чому ви хочете використовувати цей незрозумілий статичний метод замість старого доброго addAll.
Том Хотін - тайклін

8

Я вважаю, що використання AddAll працює чудово.

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

використовуються дужки, а не синтаксис generics


5
Це буде працювати для Strings, але не для змінних об'єктів. Ви також хочете їх клонувати.
jodonnell

Так, його питання стосується струн. І у нього є проблема дженериків, які не дуже люблять кастинг в даному випадку.
Аллайн Лалонде

Крім того, ArrayList.clone буде робити лише неглибокий клон, тому змінні об’єкти у списку також не будуть клоновані за допомогою цього методу.
pkaeding

Це має бути ArrayList <String> btw. Крім того, вам, мабуть, краще використовувати новий ArrayList <String> (оригінал), оскільки він менше пише і так само чітко.
cdmckay

4
Чому б просто не використовувати new ArrayList<String>(original)?
користувач102008

6

Якщо ви хочете, щоб це було в змозі повернути Список в геттері, краще зробити:

ImmutableList.copyOf(list);

4

Щоб клонувати такий загальний інтерфейс, як java.util.Listвам просто, потрібно його передати. ось вам приклад:

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

Це трохи хитро, але це працює, якщо ви обмежуєте повернення Listінтерфейсу, тож кожен, хто зможе реалізувати свій список, коли захоче.

Я знаю, що ця відповідь близька до остаточної відповіді, але моя відповідь дає відповіді на те, як все це робити, коли ви працюєте з List-загальним батьком-ArrayList


2
ви припускаєте, що це завжди буде ArrayList, що не відповідає дійсності
jucardi

@JuanCarlosDiaz, це питання, про яке було задано, мова йшла про ArrayList, тому я відповів на це за ArrayList :)
Ахмед Хемді

2

Будьте дуже обережні, клонуючи ArrayLists. Клонування в Яві неглибоке. Це означає, що він буде клонувати лише самого Arraylist, а не його членів. Отже, якщо у вас є ArrayList X1 і клонує його в X2, будь-яка зміна X2 також проявиться в X1 і навпаки. Коли ви клонуєте, ви генеруєте лише новий ArrayList з покажчиками на ті самі елементи в оригіналі.


2

Це також має працювати:

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

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

Майте на увазі, що це лише неглибока, а не глибока копія, тобто. Ви отримуєте новий список, але записи однакові. Це не проблема для просто рядків. Будьте складнішими, коли записи списку - це самі об'єкти.



1

Я не професіонал java, але у мене така ж проблема і я намагався вирішити цим методом. (Припустимо, що T має конструктор копій).

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

Немає гарантії, що Listклас реалізації має публічний конструктор no-arg. Наприклад, Lists повертається Array.asList, List.ofабо, List.subListймовірно, ні.
Том Хотін - тайклін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.