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

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

1. обзор

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

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

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

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

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

Collection srcCollection;

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

ArrayList newList;

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

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

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

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

  • Порядок такой же, как в исходной коллекции.

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

4. Использование Streams API

Теперьlet’s take advantage of the Streams API to create an ArrayList from an existing Collection: _ _

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

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

  • Берем поток из исходной коллекции и применяем операторcollect() для созданияList

  • Мы указываемArrayList::new, чтобы получить нужный нам тип списка

  • Этот код также создаст мелкую копию.

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

List newList = srcCollection.stream().collect(toList());

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

5. Deep Copy

Прежде чем мы упомянули «мелкие копии». Под этим мы подразумеваем теthe elements in the new list are exactly the same Foo instances, которые все еще существуют в исходной коллекции. Поэтому мы скопировалиFoos вnewList по ссылке.

Если мы изменим содержимое экземпляраFoo в любой коллекции, тоmodification will be reflected in both collections. Следовательно, если мы хотим изменить элементы в одной из коллекцийwithout, изменяя другую, нам нужно выполнить «глубокую копию».

Чтобы глубоко скопироватьFoo, мыcreate a completely new Foo instance for each element. Следовательно, все поля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. Эти типы данных копируются по значению. Следовательно, мы можем просто назначить их обоих.

Полеparent - это еще одинFoo, который является классом. ЕслиFoo  был изменен, любой код, который использует эту ссылку, будет затронут этими изменениями. We have to deep copy the parent field.

Теперь мы можем вернуться к нашему преобразованиюArrayList. We just need the map operator to insert the deep copy в поток:

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

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

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

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

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

Если мы хотим изменить этот порядокwe could apply the sorted() operator to the stream. Чтобы отсортировать наши объектыFoo по имени:

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

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

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

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

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

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