Перейти к основному содержимому

Преобразование коллекции в ArrayList в Java

· 4 мин. чтения

1. Обзор

Преобразование коллекций Java из одного типа в другой — обычная задача программирования. В этом руководстве мы преобразуем любой тип Collection в ArrayList .

На протяжении всего руководства мы будем предполагать, что у нас уже есть коллекция объектов Foo . Оттуда мы создадим ArrayList , используя различные подходы.

2. Определение нашего примера

Но прежде чем продолжить, давайте смоделируем наши входные и выходные данные.

Нашим источником может быть любой тип коллекции, поэтому мы объявим его с помощью интерфейса Collection :

Collection<Foo> srcCollection;

Нам нужно создать ArrayList с тем же типом элемента:

ArrayList<Foo> newList;

3. Использование конструктора ArrayList

Самый простой способ скопировать коллекцию в новую коллекцию — использовать ее конструктор.

В нашем предыдущем руководстве по ArrayList мы узнали, что конструктор ArrayList может принимать параметр коллекции:

ArrayList<Foo> newList = new ArrayList<>(srcCollection);
  • Новый ArrayList содержит поверхностную копию элементов Foo из исходной коллекции.
  • Порядок такой же, как и в исходной коллекции.

Простота конструктора делает его отличным вариантом для большинства сценариев.

4. Использование потокового API

Теперь давайте воспользуемся Streams API для создания ArrayList из существующей коллекции : ``

ArrayList<Foo> newList = srcCollection.stream().collect(toCollection(ArrayList::new));

В этом фрагменте:

  • Мы берем поток из исходной коллекции и применяем оператор collect() для создания списка .
  • Мы указываем ArrayList::new , чтобы получить нужный тип списка.
  • Этот код также создаст неглубокую копию.

Если бы нас не заботил точный тип списка , мы могли бы упростить:

List<Foo> newList = srcCollection.stream().collect(toList());

Обратите внимание, что toCollection() и toList() статически импортируются из Collectors . Чтобы узнать больше, обратитесь к нашему руководству по коллекторам Java 8 .

5. Глубокое копирование

Прежде чем мы упомянули «мелкие копии». Под этим мы подразумеваем, что элементы в новом списке — это точно такие же экземпляры Foo , которые все еще существуют в исходной коллекции. Поэтому мы скопировали Foo в newList по ссылке.

Если мы изменим содержимое экземпляра Foo в любой из коллекций, эта модификация будет отражена в обеих коллекциях . Следовательно, если мы хотим изменить элементы в одной коллекции , не изменяя другую, нам нужно выполнить «глубокую копию».

Для глубокого копирования Foo мы создаем совершенно новый экземпляр Foo для каждого элемента . Следовательно, все поля Foo необходимо скопировать в новые экземпляры.

Давайте определим наш класс Foo , чтобы он знал, как глубоко копировать себя:

public class Foo {

private int id;
private String name;
private Foo parent;

public Foo(int id, String name, Foo parent) {
this.id = id;
this.name = name;
this.parent = parent;
}

public Foo deepCopy() {
return new Foo(
this.id, this.name, this.parent != null ? this.parent.deepCopy() : null);
}
}

Здесь мы видим, что поля id и name имеют тип int и String . Эти типы данных копируются по значению. Следовательно, мы можем просто назначить их обоих.

Родительское поле — это еще одно поле Foo , которое является классом. Если Foo подвергнется мутации, эти изменения затронут любой код, который использует эту ссылку. Мы должны глубоко скопировать родительское поле .

Теперь мы можем вернуться к нашему преобразованию ArrayList . Нам просто нужен оператор карты , чтобы вставить глубокую копию в поток:

ArrayList<Foo> newList = srcCollection.stream()
.map(foo -> foo.deepCopy())
.collect(toCollection(ArrayList::new));

Мы можем изменить содержимое любой коллекции, не затрагивая другую.

Глубокая копия может быть длительным процессом в зависимости от количества элементов и глубины данных. Использование параллельного потока здесь может обеспечить повышение производительности, если это необходимо.

6. Управление порядком списка

По умолчанию наш поток доставляет элементы в наш ArrayList в том же порядке, в котором они встречаются в исходной коллекции.

Если мы хотим изменить этот порядок, мы можем применить к потоку оператор sorted() . Чтобы отсортировать наши объекты Foo по имени:

ArrayList<Foo> newList = srcCollection.stream()
.sorted(Comparator.comparing(Foo::getName))
.collect(toCollection(ArrayList::new));

Мы можем найти более подробную информацию об упорядочении потоков в этом предыдущем руководстве .

7. Заключение

Конструктор ArrayList — это эффективный способ получить содержимое коллекции в новый список ArrayList .

Однако, если нам нужно настроить полученный список, Streams API предоставляет мощный способ изменить процесс.

Код, использованный в этой статье, полностью можно найти на GitHub .