Java ArrayList копия

214

У меня есть ArrayList l1размер 10. Я назначаю l1новому списку ссылочный тип l2. Будет l1и l2указывать на тот же ArrayListобъект? Или копия ArrayListобъекта назначена l2?

При использовании l2ссылки, если я обновляю объект списка, он также отражает изменения в l1типе ссылки.

Например:

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

List l2 = l1;
l2.clear();

Нет ли другого способа назначить копию объекта списка новой ссылочной переменной, кроме создания 2 объектов списка и копирования всех коллекций из старого в новое?

user309281
источник

Ответы:

460

Да, назначение будет просто скопировать значение из l1(который является ссылкой) на l2. Они оба будут ссылаться на один и тот же объект.

Однако создать поверхностную копию довольно просто:

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

(Просто в качестве одного примера.)

Джон Скит
источник
1
Можно ли скопировать только часть массива в новый, Эффективно. например: скопировать элементы между позициями 5 и 10 из одного массива в другой новый. В моем приложении диапазон будет гораздо больше.
Эшвин
1
@ Эшвин: Ну, это операция O (N), но да ... вы можете использовать, List.subListчтобы "просмотреть" раздел исходного списка.
Джон Скит
Что делать, если списки массивов являются вложенными (ArrayList<ArrayList<Object>>)? будет ли это рекурсивно создавать копии всех дочерних объектов ArrayList?
Кот
3
@Cat: Нет ... Это всего лишь мелкая копия.
Джон Скит
1
@ShanikaEdiriweera: Да, вы можете бегло делать это с потоками. Но сложная часть заключается в создании глубокой копии, которую большинство объектов не предоставят. Если вы имеете в виду конкретный случай, я предлагаю вам задать новый вопрос с деталями.
Джон Скит
67

Попробуй использовать Collections.copy(destination, source);

Сергей Загрийчук
источник
14
Хотите объяснить, почему это может быть предпочтительнее new ArrayList<>(source);?
Алекс
6
@atc - это еще один способ делать поверхностное копирование, вместо нового ArrayList () он использует другой алгоритм и может использоваться для любой реализации List, а не только для ArrayList, вот и все :)
Сергей Загрийчук
14
Этот метод очень вводит в заблуждение! На самом деле, это описание. Он говорит: «копирует элементы из одного списка источников в место назначения», но они не копируются! На них ссылаются, поэтому будет только 1 копия объектов, и если они изменчивы, у вас проблемы
ACV
5
нигде в java-api глубокое клонирование не выполняется ни одним классом коллекции
Викаш
Этот ответ не имеет большого смысла. Collections.copyэто совсем не альтернатива new ArrayList<>(source). Что на Collections.copyсамом деле делает, так это предполагает, что destination.size()он по крайней мере такой же большой, как source.size()и, затем копирует индекс по индексу диапазона, используя set(int,E)метод. Метод не добавляет новые элементы к месту назначения. Обратитесь к исходному коду, если он недостаточно понятен из Javadoc.
Radiodef
35

Да l1и l2будет указывать на ту же ссылку, тот же объект.

Если вы хотите создать новый ArrayList на основе другого ArrayList, вы делаете это:

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

Результат будет по- l1прежнему иметь 2 элемента и l2будет иметь 3 элемента.

Альфредо Осорио
источник
Можете ли вы объяснить разницу между List<String> l2 = new ArrayList<String>(l1)и List<String> l2 = l1?
MortalMan
@MortalMan Разница в том, что l2 = new ArrayList <String> (l1) является совершенно новым объектом, и изменение l2 не влияет на l1, тогда как List <String> l2 = l1 вы не создаете новый объект, а просто ссылаетесь на тот же объект как l1, поэтому в этом случае выполнение операции, такой как l2.add («Everybody»), l1.size () и l2.size (), вернет 3, потому что оба ссылаются на один и тот же объект.
Альфредо Осорио
19

Другой удобный способ скопировать значения из src ArrayList в dest Arraylist заключается в следующем:

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

Это фактическое копирование значений, а не просто копирование ссылок.

Харшал Вагмар
источник
10
Я не совсем уверен, что это точно. мой тест показывает обратное (все еще ссылаясь на тот же объект)
инвертиго
это решение работало для меня при использовании ArrayList с ArrayAdapter
albanx
1
Этот ответ неверен. addAll () просто копирует ссылки, как сказал инвертиго. Это не глубокая копия.
jk7
Для ArrayList <String> этот ответ приемлем, потому что String неизменен, но попробуйте его с примером OP, ArraList <Integer>, и вы увидите, что он просто копирует ссылки.
jk7
Просто не мой день, я думаю. Оказывается, такие классы, как Integer и Long, также являются неизменяемыми, поэтому ответ Харшала работает для простых случаев, таких как ArrayList <Integer> и ArrayList <String>. Где это терпит неудачу, для сложных объектов, которые не являются неизменяемыми.
jk7
8

Существует метод addAll (), который будет использоваться для копирования одного ArrayList в другой.

Например, у вас есть два списка массивов: sourceList и targetList , используйте приведенный ниже код.

targetList.addAll (SOURCELIST);

Вайбхав Агравал
источник
он также просто копирует ссылки.
Викаш
4

Java не передает объекты, она передает ссылки (указатели) на объекты. Так что да, l2 и l1 - два указателя на один и тот же объект.

Вы должны сделать явную копию, если вам нужны два разных списка с одинаковым содержимым.

Дж. Б. Низет
источник
3
Как вы делаете "явную копию"? Я полагаю, вы говорите о глубокой копии?
Cin316
1

List.copyOf ➙ неизменяемый список

Ты спрашивал:

Нет ли другого способа назначить копию списка?

В Java 9 появились List.ofметоды использования литералов для создания неизменяемого Listнеизвестного конкретного класса.

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

Наряду с этим мы также получили List.copyOf. Этот метод также возвращает неизменяемый Listнеизвестный конкретный класс.

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`.

Под «немодифицируемым» мы подразумеваем количество элементов в списке, а референт объекта, который содержится в каждом слоте как элемент, является фиксированным. Вы не можете добавлять, удалять или заменять элементы. Но объектный референт, содержащийся в каждом элементе, может быть или не быть изменяемым .

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

Смотрите этот код в прямом эфире на IdeOne.com .

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

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

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

Вы спрашивали об объектных ссылках. Как говорили другие, если вы создаете один список и назначаете его двум ссылочным переменным (указателям), у вас все равно остается только один список. Оба указывают на один и тот же список. Если вы используете любой указатель для изменения списка, оба указателя позже увидят изменения, так как в памяти есть только один список.

Так что вам нужно сделать копию списка. Если вы хотите, чтобы эта копия не изменялась, используйте List.copyOfметод, описанный в этом ответе. При таком подходе вы получите два отдельных списка, каждый из которых содержит элементы, которые содержат ссылку на одни и те же объекты содержимого. Например, в нашем примере выше, использующем Stringобъекты для представления цветов, цветные объекты плавают где-то в памяти. Два списка содержат указатели на объекты одного цвета. Вот схема.

введите описание изображения здесь

Первый список colorsможно изменить. Это означает, что некоторые элементы могут быть удалены, как видно из кода выше, где мы удалили исходный третий элемент Chartreuse(индекс 2 = порядковый номер 3). И элементы могут быть добавлены. И элементы могут быть изменены, чтобы указать на некоторые другие, Stringтакие как OliveDrabилиCornflowerBlue .

Напротив, четыре элемента masterColorsявляются фиксированными. Без удаления, без добавления и без замены другого цвета. Эта Listреализация не поддается изменению.

Базилик Бурк
источник