1. Обзор
Преобразование коллекций Java из одного типа в другой-обычная задача программирования. В этом уроке мы преобразуем любой тип Collection в ArrayList .
На протяжении всего урока мы будем предполагать, что у нас уже есть коллекция объектов Foo . Оттуда мы создадим ArrayList , используя различные подходы.
2. Определение Нашего Примера
Но прежде чем продолжить, давайте смоделируем наши входные и выходные данные.
Наш источник может быть любым типом коллекции, поэтому мы объявим его с помощью интерфейса Collection :
CollectionsrcCollection;
Нам нужно создать ArrayList с тем же типом элемента:
ArrayListnewList;
3. Использование конструктора ArrayList
Самый простой способ скопировать коллекцию в новую коллекцию-это использовать ее конструктор.
В нашем предыдущем руководстве по ArrayList мы узнали , что конструктор ArrayList может принимать параметр коллекции:
ArrayListnewList = new ArrayList<>(srcCollection);
- Новый ArrayList содержит неглубокую копию элементов Foo в исходной коллекции.
- Порядок такой же, как и в исходной коллекции.
Простота конструктора делает его отличным вариантом в большинстве сценариев.
4. Использование API потоков
Теперь давайте воспользуемся API Streams для создания списка массивов из существующей коллекции :
ArrayListnewList = srcCollection.stream().collect(toCollection(ArrayList::new));
В этом фрагменте:
- Мы берем поток из исходной коллекции и применяем оператор collect() для создания списка
- Мы указываем ArrayList::new , чтобы получить нужный нам тип списка
- Этот код также создаст неглубокую копию.
Если бы мы не беспокоились о точном типе List , мы могли бы упростить:
ListnewList = srcCollection.stream().collect(toList());
Обратите внимание, что to Collection() и to List() статически импортируются из Collectors . Чтобы узнать больше, пожалуйста, обратитесь к нашему руководству по сборщикам Java 8 .
5. Глубокое Копирование
Прежде чем мы упомянули о “неглубоких копиях”. Под этим мы подразумеваем, что элементы в новом списке являются точно такими же Foo экземплярами , которые все еще существуют в исходной коллекции. Поэтому мы скопировали Foo s в новый список по ссылке.
Если мы изменим содержимое экземпляра 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 are int и String . Эти типы данных копируются по значению. Следовательно, мы можем просто назначить их обоих.
Поле parent – это другое Foo , которое является классом. Если Foo мутировал, любой код, который использует эту ссылку, будет затронут этими изменениями. Мы должны глубоко скопировать родительское поле .
Теперь мы можем вернуться к нашему ArrayList преобразованию. Нам просто нужен оператор map , чтобы вставить глубокую копию в поток:
ArrayListnewList = srcCollection.stream() .map(foo -> foo.deepCopy()) .collect(toCollection(ArrayList::new));
Мы можем изменять содержимое любой коллекции, не затрагивая другую.
Глубокое копирование может быть длительным процессом в зависимости от количества элементов и глубины данных. Использование параллельного потока здесь может обеспечить повышение производительности, если это необходимо.
6. Управление порядком списка
По умолчанию ваш поток будет доставлять элементы в наш ArrayList в том же порядке, в каком они встречаются в исходной коллекции.
Если мы хотим изменить этот порядок , мы можем применить оператор sorted() к потоку . Чтобы отсортировать Foo объекты по имени:
ArrayListnewList = srcCollection.stream() .sorted(Comparator.comparing(Foo::getName)) .collect(toCollection(ArrayList::new));
Более подробную информацию о заказе потоков мы можем найти в этом более раннем руководстве .
7. Заключение
Конструктор ArrayList – это эффективный способ получить содержимое Коллекции в новый ArrayList .
Однако, если нам нужно настроить результирующий список, API потоков предоставляет мощный способ изменить процесс.
Код, используемый в этой статье, можно найти полностью на GitHub .