В данной статье мы расскажем вам, как находить дублирующиеся элементы в стримах. Допустим, у вас есть некий стрим с элеменетами, которые могут повторяться, и у вам нужно найти такие элементы. Эту задачу можно решить несколькими способами.

Варианты решения задачи

Есть несколько вариантов найти дубликаты в стриме:

  • С помощью дополнительного объекта 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. Но если вам не важна скорость работы, вы можете выбрать любой подходящий вам способ для решения задачи.

Поиск дубликатов в Stream