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

1. Общ преглед

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

В целия урок ще приемем, че вече имаме колекция от Foo обекти. Оттам нататък ще създадем ArrayList, използвайки различни подходи.

2. Дефиниране на нашия пример

Но преди да продължим, нека моделираме нашия вход и изход.

Нашият източник може да бъде всякакъв тип колекция, така че ще го декларираме чрез интерфейса на колекцията :

Collection srcCollection; 

Трябва да създадем ArrayList със същия тип елемент:

ArrayList newList;

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

Най-простият начин за копиране на колекция в нова колекция е използването на нейния конструктор.

В предишното ни ръководство за ArrayList научихме, че конструкторът ArrayList може да приеме параметър за събиране:

ArrayList newList = new ArrayList(srcCollection);
  • Новият ArrayList съдържа плитко копие на елементите Foo в колекцията източник.
  • Поръчката е същата като тази в колекцията източници.

Простотата на конструктора го прави чудесен вариант в повечето сценарии.

4. Използване на API за потоци

Сега, нека се възползваме от Streams API, за да създадем ArrayList от съществуваща колекция :

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

В този фрагмент:

  • Вземаме потока от колекцията източник и прилагаме оператора collect () , за да създадем Списък
  • Държим да уточним ArrayList :: ново , за да получите вида на списъка, което искаме
  • Този код също ще създаде плитко копие.

Ако не бяхме загрижени за точния тип Списък , бихме могли да опростим:

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

Имайте предвид, че toCollection () и toList () са статично импортирани от Колекционери . За да научите повече, моля, направете справка с нашето ръководство за Колекционерите на Java 8.

5. Дълбоко копиране

Преди да споменем „плитки копия“. С това имаме предвид, че елементите в новия списък са абсолютно същите екземпляри на Foo, които все още съществуват в колекцията източник. Следователно копирахме Foo s в 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 newList = srcCollection.stream() .map(foo -> foo.deepCopy()) .collect(toCollection(ArrayList::new));

Можем да модифицираме съдържанието на която и да е колекция, без да засягаме другата.

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

6. Контролиране на реда на списъка

По подразбиране нашият поток ще доставя елементи към нашия ArrayList в същия ред, в който се срещат в колекцията източник.

Ако искаме да променим този ред, можем да приложим оператора sorted () към потока . За да сортирате нашите Foo обекти по име:

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

Можем да намерим допълнителни подробности за поръчването на потоци в този по-ранен урок.

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

Конструкторът ArrayList е ефективен начин за преместване на съдържанието на Колекция в нов ArrayList .

Ако обаче трябва да променим получения списък, API за потоци предоставя мощен начин за модифициране на процеса.

Кодът, използван в тази статия, може да бъде намерен изцяло в GitHub.