1. Обзор
В этом кратком руководстве мы поговорим о четырех различных способах удаления элементов из Java Collections , которые соответствуют определенным предикатам.
Естественно, мы также рассмотрим некоторые предостережения.
2. Определение нашей коллекции
Во-первых, мы собираемся проиллюстрировать два подхода, которые изменяют исходную структуру данных. Затем мы поговорим о двух других вариантах, которые вместо удаления элементов создадут копию оригинальной коллекции Collection без них.
Давайте используем следующую коллекцию в наших примерах, чтобы продемонстрировать, как мы можем достичь того же результата, используя разные методы:
Collection<String> names = new ArrayList<>();
names.add("John");
names.add("Ana");
names.add("Mary");
names.add("Anthony");
names.add("Mark");
3. Удаление элементов с Iterator
Https://www.baeldung.com/java-iterator[Iterator] в Java позволяет нам как обходить, так и удалять каждый отдельный элемент в Collection .**
Для этого сначала нужно извлечь итератор для его элементов с помощью метода iterator . , После этого мы можем посетить каждый элемент с помощью next и удалить их с помощью remove :
Iterator<String> i = names.iterator();
while(i.hasNext()) {
String e = i.next();
if (e.startsWith("A")) {
i.remove();
}
}
Несмотря на свою простоту, есть несколько предостережений, которые следует учитывать:
-
** В зависимости от коллекции мы можем запустить
в ConcurrentModificationException исключения Нам нужно перебрать элементы, прежде чем мы сможем удалить их
-
В зависимости от коллекции, remove может вести себя иначе, чем
ожидается. Например: ArrayList.Iterator удаляет элемент из коллекции и перемещает последующие данные влево, тогда как LinkedList.Iterator просто настраивает указатель на следующий элемент. Таким образом, LinkedList.Iterator работает намного лучше, чем ArrayList.Iterator при удалении элементов.
4. Java 8 и Collection.removeIf ()
В Java 8 появился новый метод интерфейса Collection , который обеспечивает более краткий способ удаления элементов с помощью Предикат _: _
names.removeIf(e -> e.startsWith("A"));
Важно отметить, что в отличие от подхода Iterator removeIf одинаково хорошо работает как в LinkedList , так и в ArrayList .
В Java 8 ArrayList переопределяет реализацию по умолчанию - которая опирается на Iterator - и реализует другую стратегию: во-первых, он перебирает элементы и помечает элементы, которые соответствуют нашему Predicate; после, он повторяет второй раз для удаления (и сдвига ) элементы, которые были отмечены в первой итерации.
5. Java 8 и введение Stream
Одной из новых основных функций в Java 8 стало добавление Stream (и Collectors ).
Есть много способов создать Stream из источника. Тем не менее, большинство операций, которые влияют на экземпляр Stream , не изменяют его источник, скорее, API фокусируется на создании копий источника и выполнении любой операции, которая может нам понадобиться.
Давайте посмотрим, как мы можем использовать Stream и Collectors , чтобы находить/фильтровать элементы, которые соответствуют, а не совпадают с нашим Predicate .
5.1. Удаление элементов с помощью Stream
Удаление или, точнее, фильтрация элементов с помощью Stream довольно проста , нам просто нужно создать экземпляр Stream с помощью нашего Collection , вызвать filter с нашим Predicate , а затем https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html # collect-java.util.stream.Collector -[ collect ]результат с помощью Collectors:
Collection<String> filteredCollection = names
.stream()
.filter(e -> !e.startsWith("A"))
.collect(Collectors.toList());
Streaming менее инвазивен, чем предыдущие подходы, он способствует изоляции и позволяет создавать несколько копий из одного источника. Однако следует помнить, что это также увеличивает объем памяти, используемой нашим приложением.
5.2. Collectors.partitioningBy
Комбинация Stream.filter и Collectors довольно удобна, хотя мы можем столкнуться с сценариями, в которых нам нужны как совпадающие, так и несовпадающие элементы. В таких случаях мы можем воспользоваться https://docs.oracle.com/javase/8/документы/API/Java/Util/поток/Collectors.html # partitioningBy-java.util.function.Predicate-[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));
Этот метод возвращает Map , который содержит только два ключа, true и false , каждый из которых указывает на список, который содержит совпадающие и несоответствующие элементы, соответственно.
6. Заключение
В этой статье мы рассмотрели некоторые методы удаления элементов из Collections и некоторые их предостережения.
Вы можете найти полный исходный код и все фрагменты кода для этой статьи over на GitHub .