Удаление элементов из коллекций Java

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 .