1. Обзор
В этом кратком руководстве мы поговорим о четырех различных способах удаления элементов из коллекций
Java , которые соответствуют определенным предикатам.
Естественно, мы также рассмотрим некоторые предостережения.
2. Определение нашей коллекции
Во-первых, мы проиллюстрируем два подхода, изменяющих исходную структуру данных. Затем мы поговорим о двух других вариантах, которые вместо удаления элементов создадут копию исходной коллекции
без них.
Давайте используем следующую коллекцию в наших примерах, чтобы продемонстрировать, как мы можем достичь одного и того же результата, используя разные методы:
Collection<String> names = new ArrayList<>();
names.add("John");
names.add("Ana");
names.add("Mary");
names.add("Anthony");
names.add("Mark");
3. Удаление элементов с помощью итератора
Iterator
в Java позволяет нам как просматривать, так и удалять каждый отдельный элемент в коллекции
.
Для этого нам сначала нужно получить итератор по его элементам, используя метод итератора
. После этого мы можем посетить каждый элемент с помощью next
и удалить их с помощью remove
:
Iterator<String> i = names.iterator();
while(i.hasNext()) {
String e = i.next();
if (e.startsWith("A")) {
i.remove();
}
}
Несмотря на его простоту, есть некоторые предостережения, которые мы должны учитывать:
- В зависимости от коллекции мы можем столкнуться с исключениями
ConcurrentModificationException .
- Нам нужно перебрать элементы, прежде чем мы сможем их удалить.
- В зависимости от коллекции
удаление
может вести себя не так, как ожидалось. Например:ArrayList.Iterator
удаляет элемент из коллекции и сдвигает последующие данные влево, тогда какLinkedList.Iterator
просто настраивает указатель на следующий элемент. Таким образом,LinkedList.Iterator
работает намного лучше, чемArrayList.Iterator
при удалении элементов .
4. Java 8 и Collection.removeIf()
Java 8 представила новый метод интерфейса Collection
, который обеспечивает более краткий способ удаления элементов с помощью Predicate
:
names.removeIf(e -> e.startsWith("A"));
Важно отметить, что в отличие от подхода Iterator ,
removeIf
одинаково хорошо работает как в LinkedList
, так и в ArrayList
.
В Java 8 ArrayList
переопределяет реализацию по умолчанию, основанную на Iterator
, и реализует другую стратегию: во-первых, он перебирает элементы и отмечает те, которые соответствуют нашему предикату;
после этого он выполняет вторую итерацию, чтобы удалить (и сдвинуть) элементы, которые были отмечены в первой итерации.
5. Java 8 и введение Stream
Одной из новых важных функций в Java 8 стало добавление Stream
(и Collectors
). Существует много способов создать поток
из источника. Однако большинство операций, влияющих на экземпляр Stream
, не изменяют его источник, а скорее API фокусируется на создании копий источника и выполнении в них любых операций, которые нам могут понадобиться.
Давайте посмотрим, как мы можем использовать Stream
и Collectors
для поиска/фильтрации элементов, которые соответствуют и не соответствуют нашему Predicate
.
5.1. Удаление элементов с помощью потока
Удаление, а точнее, фильтрация элементов с помощью Stream
довольно проста , нам просто нужно создать экземпляр Stream
с помощью нашей Collection
, вызвать filter
с помощью нашего Predicate
и затем собрать
результат с помощью Collectors:
Collection<String> filteredCollection = names
.stream()
.filter(e -> !e.startsWith("A"))
.collect(Collectors.toList());
Потоковая передача
менее инвазивна, чем предыдущие подходы, она способствует изоляции и позволяет создавать несколько копий из одного и того же источника. Однако мы должны помнить, что это также увеличивает объем памяти, используемой нашим приложением.
5.2. Collectors.partitioningBy
Комбинация Stream.filter
и Collectors
весьма удобна, хотя мы можем столкнуться со сценариями, в которых нам нужны как совпадающие, так и несовпадающие элементы. В таких случаях мы можем воспользоваться Collectors.partitioningBy
:
Map<Boolean, List<String>> classifiedElements = names
.stream()
.collect(Collectors.partitioningBy((String e) ->
!e.startsWith("A")));
String matching = String.join(",",
classifiedElements.get(true));
String nonMatching = String.join(",",
classifiedElements.get(false));
Этот метод возвращает карту
, содержащую только два ключа, true
и false
, каждый из которых указывает на список, содержащий совпадающие и не совпадающие элементы соответственно.
6. Заключение
В этой статье мы рассмотрели некоторые методы удаления элементов из коллекций
и некоторые их предостережения.
Вы можете найти полный исходный код и все фрагменты кода для этой статьи на GitHub .