Как мне клонировать общий список в Java?

155

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

public Object clone()

После того, как я вызову этот метод, как я приведу возвращенный объект обратно ArrayList<String>?

Билл Ящерица
источник
16
Нет, это правильный вопрос. Java не поддерживает «истинные» обобщения, со стиранием типов во время выполнения и прочим, поэтому такие детали могут быть хитрыми. Кроме того, интерфейс Cloneable и механизм метода Object.clone () также сбивают с толку.
Outlaw Programmer
Хорошо, я в основном делаю C #, где это действительно легко. Пожалуйста, дайте мне знать, если вы хотите, чтобы я удалил комментарии к этому вопросу.
Espo
1
Вы можете оставить комментарий. Я думаю, что мои правки объяснили, с чем у меня были проблемы.
Билл Ящерица
2
Ваш комментарий в порядке, если немного снисходительный. Я представляю, что многие проблемы, через которые приходится проходить Java-разработчикам, кажутся глупыми .NET-разработчикам.
Outlaw Programmer
1
@ Оскар, он хочет клонировать, а не вызывать команду клонирования. Возможно, это не то же самое, что клон на самом деле не клонирует. Я думаю, что это точка. Это действительно сложный вопрос.
Рафа

Ответы:

62
ArrayList newArrayList = (ArrayList) oldArrayList.clone();
Винко Врсалович
источник
43
Это будет хорошо работать со строками (что и было задано в вопросе), но стоит отметить, что ArrayList.clone выполнит поверхностную копию, поэтому, если в списке были изменяемые объекты, они не будут клонированы (и изменяют один). в одном списке это тоже изменится в другом списке
pkaeding
49
Вы должны избегать использования необработанных типов во всем, кроме устаревшего кода. Вам лучше использовать ArrayList<String> newArrayList = (ArrayList<String>) oldArrayList.clone();.
cdmckay
20
Жаль, что у ArrayList есть метод #clone, а в самом List нет. Вздох.
rogerdpack
12
Не связано, но пока у него: используйте List<String>вместо ArrayList<String>левой стороны. Именно так коллекции должны использоваться большую часть времени.
Кристоф Русси
3
Я не мог использовать этот метод для дублирования ArrayList. Метод clone () не был распознан.
Джек,
318

Почему вы хотите клонировать? Создание нового списка обычно имеет больше смысла.

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

Работа выполнена.

Том Хотин - Tackline
источник
17
Вы можете не знать, что это за список. Это может быть LinkedList, MyOwnCustomList или подкласс ArrayList, и в этом случае добавление ArrayList будет неправильным типом.
Стив Куо
51
Мне было бы интересно, какую реализацию использовал оригинальный список? Мне, наверное, важно, какую реализацию использует новый список.
Том Хотин - tackline
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конструктора?
Том Хотин - tackline
19

С Java 8 это может быть клонировано с потоком.

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

...

List<AnObject> clone = myList.stream().collect(toList());
Саймон Дженкинс
источник
Может быть сделано как List <AnObject> xy = new ArrayList <> (oldList);
Мирзак
1
Это не
полная
2
Где в вопросе указано, что требуется глубокая копия? Предполагается, что требуется другая коллекция, содержащая те же объекты. Если вы хотите пойти по пути глубокого копирования, вы открываете новую банку с червями.
Саймон Дженкинс
Не правда ли клон, правда? Из документации API "Нет никаких гарантий относительно типа, изменчивости, сериализуемости или безопасности потока возвращаемого списка". Эй, не хочу один из них. toCollectionможет быть лучшим выбором
Том Хотин - тэклайн
16

Имейте в виду, что Object.clone () имеет некоторые серьезные проблемы, и его использование не рекомендуется в большинстве случаев. Пожалуйста, см. Пункт 11 из « Эффективной Явы » Джошуа Блоха для полного ответа. Я полагаю, что вы можете безопасно использовать Object.clone () в массивах примитивного типа, но помимо этого вы должны быть осторожны в правильном использовании и переопределении клона. Возможно, вам лучше определить конструктор копирования или статический метод фабрики, который явно клонирует объект в соответствии с вашей семантикой.

Жюльен Частанг
источник
15

Я думаю, что это должно сработать, используя Collections API:

Примечание : метод копирования выполняется за линейное время.

//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 не то, чтобы я согласился, что это лучший способ, но это способ сделать это. Чтобы обойти вашу проблему, она такая же, как эта: List <String> newList = new ArrayList <> (oldList.size ());
nckbrz
1
Не уверен, связано ли это с Java 8, но даже при указании размера все равно получаю исключение IndexOutOfBoundsException: destination.size () <source.size (): 0 <2 on List<MySerializableObject> copyList = new ArrayList<>(mMySerializableObjects.size()); Кажется, использование copyList.addAll(original);- хорошая альтернатива
Джин Бо
@GeneBo Размер не указан. Он определяет емкость, а это совсем другое. Радость загадочных intаргументов. Я понятия не имею, почему вы хотите использовать этот неясный статический метод вместо старого addAll.
Том Хотин -
8

Я нахожу, используя addAll работает отлично.

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

используются скобки, а не синтаксис обобщения

Аллен Лалонд
источник
5
Это будет работать для строк, но не для изменяемых объектов. Вы также захотите их клонировать.
Джодоннелл
Да, его вопрос для строк. И у него есть проблема с дженериками, которым в данном случае не очень нравится кастинг.
Аллен Лалонд
Кроме того, ArrayList.clone будет выполнять только мелкий клон, поэтому изменяемые объекты в списке также не будут клонироваться с использованием этого метода.
pkaeding
Это должно быть ArrayList <String>, кстати. Кроме того, вам, вероятно, лучше использовать новый ArrayList <String> (оригинал), так как он меньше пишет и так же ясен.
cdmckay
4
Почему бы просто не использовать new ArrayList<String>(original)?
user102008
6

Если вы хотите это для того, чтобы иметь возможность возвращать список в получателе, было бы лучше сделать:

ImmutableList.copyOf(list);
Ури Шалит
источник
4

Для клонирования общего интерфейса, подобного java.util.Listвам, нужно просто привести его. вот вам пример:

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

Это немного сложно, но это работает, если вы ограничены в возврате Listинтерфейса, так что любой после вас может реализовать свой список, когда захочет.

Я знаю, что этот ответ близок к окончательному ответу, но мой ответ отвечает, как сделать все это, пока вы работаете с Listродителем-родителем, а неArrayList

Ахмед Хамди
источник
2
вы предполагаете, что это всегда будет ArrayList, что не соответствует действительности
Джукарди
@JuanCarlosDiaz будет, это вопрос, который был задан, это было о ArrayList, поэтому я ответил на него для ArrayList :)
Ахмед
2

Будьте очень осторожны при клонировании ArrayLists. Клонирование в Java мелкое. Это означает, что он будет клонировать только самого Arraylist, а не его членов. Таким образом, если у вас есть ArrayList X1 и клонируйте его в X2, любое изменение в X2 также проявится в X1 и наоборот. При клонировании вы будете генерировать только новый ArrayList с указателями на те же элементы в оригинале.

Хуан Беса
источник
2

Это также должно работать:

ArrayList<String> orig = new ArrayList<String>();
ArrayList<String> copy = (ArrayList<String>) orig.clone()
pkaeding
источник
2
List<String> shallowClonedList = new ArrayList<>(listOfStrings);

Имейте в виду, что это только мелкая, а не глубокая копия, т.е. Вы получаете новый список, но записи совпадают. Это не проблема для простых строк. Получить еще сложнее, когда записи списка сами являются объектами.

Роберт
источник
1
ArrayList first = new ArrayList ();
ArrayList copy = (ArrayList) first.clone ();
jodonnell
источник
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);
      }
}
Petr
источник
Нет гарантии, что Listкласс реализации имеет открытый конструктор без аргументов. Например, Lists возвращает Array.asList, List.ofили, List.subListвозможно, нет.
Том Хотин -