Перейти к основному содержимому

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

· 4 мин. чтения

Задача: Медиана двух отсортированных массивов

Даны два отсортированных массива размерами n и m. Найдите медиану слияния этих двух массивов.
Временная сложность решения должна быть O(log(m + n)) ...

ANDROMEDA

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 стало добавление StreamCollectors ). Существует много способов создать поток из источника. Однако большинство операций, влияющих на экземпляр 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 .