В данной статье мы расскажем вам, как находить дублирующиеся элементы в стримах. Допустим, у вас есть некий стрим с элеменетами, которые могут повторяться, и у вам нужно найти такие элементы. Эту задачу можно решить несколькими способами.
Варианты решения задачи
Есть несколько вариантов найти дубликаты в стриме:
- С помощью дополнительного объекта Set
- С помощью метода Collectors.groupingBy
- С помощью утилитного метода Collections.frequency
Поиск дубликатов с помощью Set
Сначала инициализируем вспомогательный Set, с помощью которого мы будем проверять, является ли следующий элемент стрима дубликатом:
Set<T> elements = new HashSet<>();
Затем в стриме будем добавлять каждый элемент из коллекции в Set с помощью метода add(). Этот метод возвращает true в том случае, если этот элемент ранее не был добавлен в Set:
collection.stream() .filter(e -> !elements.add(e)) .collect(Collectors.toSet());
Таким образом, мы отфильтруем те элементы, которые встречаются в коллекции более одного раза.
Таким образом, полный код нашего метода может выглядеть следующим образом:
public static <T> Set<T> findDuplicates(Collection<T> collection) { Set<T> elements = new HashSet<>(); return collection.stream() .filter(e -> !elements.add(e)) .collect(Collectors.toSet()); }
Проверим его с помощью тестовых данных:
public static void main(String[] args) { List<String> names = Arrays.asList("Alice", "Bob", "Alice", "Carl", "Don", "Eugene", "Carl"); System.out.println("Все элементы: " + names); Set<String> duplicates = findDuplicates(names); System.out.println("Дубликаты: " + duplicates); }
В результате мы выведем на экран дублирующиеся элементы:
Поиск дубликатов с помощью Set наиболее быстрый вариант из предложенных в данной статье.
Поиск дубликатов с помощью Collectors.groupingBy
Вместо использования Set, можно решить задачу поиска дубликатов с помощью коллекторов groupingBy и counting:
public static <T> Set<T> findDuplicates(Collection<T> collection) { return collection.stream() .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())) .entrySet() .stream() .filter(e -> e.getValue() > 1) .map(Map.Entry::getKey) .collect(Collectors.toSet()); }
Коллектор groupingBy вернёт HashMap, который мы снова преобразуем в стрим и фильтруем по количеству вхождений.
Поиск дубликатов с помощью Collections.frequency
Ещё один способ поиска дубликатов в стриме — использование вспомогательного метода Collections.frequency для каждого элемента:
public static <T> Set<T> findDuplicates(Collection<T> collection) { return collection.stream() .filter(e -> Collections.frequency(collection, e) > 1) .collect(Collectors.toSet()); }
Данный метод является самым медленным по скорости работы, так как приходится обходить всю коллекцию объектов для каждого элемента.
Исходный код
import java.util.*; import java.util.stream.Collectors; public class FindDuplicatesInStream1 { public static void main(String[] args) { List<String> names = Arrays.asList("Alice", "Bob", "Alice", "Carl", "Don", "Eugene", "Carl"); System.out.println("Все элементы: " + names); Set<String> duplicates = findDuplicates(names); System.out.println("Дубликаты: " + duplicates); } public static <T> Set<T> findDuplicates(Collection<T> collection) { Set<T> elements = new HashSet<>(); return collection.stream() .filter(e -> !elements.add(e)) .collect(Collectors.toSet()); } }
import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; public class FindDuplicatesInStream2 { public static void main(String[] args) { List<String> names = Arrays.asList("Alice", "Bob", "Alice", "Carl", "Don", "Eugene", "Carl"); System.out.println("Все элементы: " + names); Set<String> duplicates = findDuplicates(names); System.out.println("Дубликаты: " + duplicates); } public static <T> Set<T> findDuplicates(Collection<T> collection) { return collection.stream() .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())) .entrySet() .stream() .filter(e -> e.getValue() > 1) .map(Map.Entry::getKey) .collect(Collectors.toSet()); } }
import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; public class FindDuplicatesInStream3 { public static void main(String[] args) { List<String> names = Arrays.asList("Alice", "Bob", "Alice", "Carl", "Don", "Eugene", "Carl"); System.out.println("Все элементы: " + names); Set<String> duplicates = findDuplicates(names); System.out.println("Дубликаты: " + duplicates); } public static <T> Set<T> findDuplicates(Collection<T> collection) { return collection.stream() .filter(e -> Collections.frequency(collection, e) > 1) .collect(Collectors.toSet()); } }
Заключение
В статье мы разобрали несколько вариантов поиска дубликатов в Java Streams. Самый быстрый их них по производительности — первый, с использованием Set. Но если вам не важна скорость работы, вы можете выбрать любой подходящий вам способ для решения задачи.