1. Обзор
В этом кратком руководстве мы рассмотрим различные способы копирования списка
в другой список
и распространенную ошибку, возникающую в процессе.
Для ознакомления с использованием коллекций
см . эту статью здесь .
2. Конструктор
Простой способ скопировать список
— использовать конструктор, который принимает коллекцию в качестве аргумента:
List<Plant> copy = new ArrayList<>(list);
Поскольку здесь мы копируем ссылки, а не клонируем объекты, каждое исправление, сделанное в одном элементе, повлияет на оба списка.
Таким образом, хорошо использовать конструктор для копирования неизменяемых объектов:
List<Integer> copy = new ArrayList<>(list);
Integer
— неизменяемый класс; его значение устанавливается при создании экземпляра и никогда не может измениться.
Таким образом, ссылка Integer
может совместно использоваться несколькими списками и потоками, и никто не может изменить ее значение.
3. Список
ConcurrentAccessException
Распространенной проблемой при работе со списками является ConcurrentAccessException
. Обычно это означает, что мы изменяем список, когда пытаемся его скопировать, скорее всего, в другом потоке.
Чтобы решить эту проблему, мы должны:
- Используйте коллекцию, предназначенную для одновременного доступа
- Заблокируйте коллекцию соответствующим образом, чтобы перебрать ее
- Найдите способ избежать копирования оригинальной коллекции
Учитывая наш последний подход, он не является потокобезопасным. Если мы хотим решить нашу проблему с помощью первого варианта, мы можем захотеть использовать CopyOnWriteArrayList
, в котором все мутативные операции реализуются путем создания новой копии базового массива.
Для получения дополнительной информации, пожалуйста, обратитесь к этой статье .
Если мы хотим заблокировать Collection
, можно использовать примитив блокировки для сериализованного доступа для чтения/записи, например ReentrantReadWriteLock
.
4. Добавить все
Другой подход к копированию элементов — использование метода addAll
:
List<Integer> copy = new ArrayList<>();
copy.addAll(list);
При использовании этого метода важно помнить, что, как и в случае с конструктором, содержимое обоих списков будет ссылаться на одни и те же объекты.
5. Коллекции.copy
Класс Collections
состоит исключительно из статических методов, которые работают с коллекциями или возвращают их.
Одним из них является copy
, для которого требуется исходный список и целевой список, длина которого не меньше исходного.
Он будет поддерживать индекс каждого скопированного элемента в списке назначения, например оригинала:
List<Integer> source = Arrays.asList(1,2,3);
List<Integer> dest = Arrays.asList(4,5,6);
Collections.copy(dest, source);
В приведенном выше примере все предыдущие элементы в списке
назначения были перезаписаны, поскольку оба списка имеют одинаковый размер.
Если размер списка назначения больше, чем размер источника:
List<Integer> source = Arrays.asList(1, 2, 3);
List<Integer> dest = Arrays.asList(5, 6, 7, 8, 9, 10);
Collections.copy(dest, source);
Здесь только первые три элемента были перезаписаны, а остальные элементы в списке сохранены.
6. Использование Java 8
Эта версия Java расширяет наши возможности, добавляя новые инструменты. В следующих примерах мы рассмотрим Stream
:
List<String> copy = list.stream()
.collect(Collectors.toList());
Главное преимущество этого варианта — возможность использовать пропуск и фильтры. В следующем примере мы пропустим первый элемент:
List<String> copy = list.stream()
.skip(1)
.collect(Collectors.toList());
Также можно фильтровать по длине строки
или сравнивать атрибуты наших объектов:
List<String> copy = list.stream()
.filter(s -> s.length() > 10)
.collect(Collectors.toList());
List<Flower> flowers = list.stream()
.filter(f -> f.getPetals() > 6)
.collect(Collectors.toList());
Вероятно, мы хотим работать нулевым безопасным способом:
List<Flower> flowers = Optional.ofNullable(list)
.map(List::stream)
.orElseGet(Stream::empty)
.collect(Collectors.toList());
Вероятно, мы также захотим пропустить элемент таким образом:
List<Flower> flowers = Optional.ofNullable(list)
.map(List::stream).orElseGet(Stream::empty)
.skip(1)
.collect(Collectors.toList());
7. Использование Java 10
Наконец, одна из последних версий Java позволяет нам создать неизменяемый список
, содержащий элементы данной коллекции:
List<T> copy = List.copyOf(list);
Единственным условием является то, что данная коллекция не должна быть нулевой или содержать нулевые элементы.
8. Заключение
В этой статье мы узнали о различных способах копирования списка
в другой список
с разными версиями Java. Мы также рассмотрели распространенную ошибку, возникающую в процессе.
Как всегда, образцы кода можно найти на GitHub, здесь и здесь .