Оптимальный поиск дубликатов в списке чисел Java

Пройдите тест, узнайте какой профессии подходите

Я предпочитаю
0%
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы

Быстрый ответ

Для поиска дубликатов в List примените Set. Это поможет отсечь повторения. Вот как это осуществить с помощью операций stream:

Java
Скопировать код
Set<Integer> uniqueItems = new HashSet<>();
List<Integer> duplicates = list.stream()
                               .filter(n -> !uniqueItems.add(n))
                               .collect(Collectors.toList());

Также, элемент добавится в uniqueItems в случае его присутствия там (то есть, если это дубликат), метод возвратит false, и после посредством filter подобные элементы будут сохранены.

Кинга Идем в IT: пошаговый план для смены профессии

Понятность и лаконичность с потоками Java 8

API потоков Java 8 предоставляет изящный способ фильтрации данных и группировки их с использованием Collectors.groupingBy. Вот наш план:

Java
Скопировать код
List<Integer> duplicateElements = list.stream() 
    .collect(Collectors.groupingBy(Function.identity(), LinkedHashMap::new, Collectors.counting()))
    .entrySet().stream()
    .filter(entry -> entry.getValue() > 1)
    .map(Map.Entry::getKey)
    .collect(Collectors.toList());

Выполняя groupingBy совместно с LinkedHashMap, мы сохраняем начальный порядок элементов, тогда как counting подсчитывает их количество. filter и map затем отбирают только те элементы, что встречаются более одного раза.

Силовые приемы: Множества и операции с потоками

Сочетание Sets и Streams приносит эффективность и высокую производительность:

Java
Скопировать код
Set<Integer> seen = ConcurrentHashMap.newKeySet();
List<Integer> duplicateNumbers = list.parallelStream()
                                     .filter(n -> !seen.add(n))
                                     .collect(Collectors.toList());

Применяется ConcurrentHashMap.newKeySet() для атомарных операций, что необходимо при работе с parallel stream, чтобы не упустить дубликаты.

Как справиться с кризисом идентичности: разбираемся с уникальными элементами и группами

Применяйте distinct и groupingBy для дифференциации элементов и выделения дубликатов:

Java
Скопировать код
Map<Integer, Long> elementCountMap = list.stream()
                                         .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

Set<Integer> duplicates = elementCountMap.entrySet().stream()
                                         .filter(e -> e.getValue() > 1)
                                         .map(Map.Entry::getKey)
                                         .collect(Collectors.toSet());

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

Визуализация

Визуализируем код, приведя аналогию с вешалкой для одежды:

Markdown
Скопировать код
| Вешалка 🧥                 | Статус             |
| -------------------------- | ------------------ |
| Синяя рубашка             | Оригинальная       |
| Белая рубашка             | Дубликат! (1-й)    |
| Синие брюки               | Оригинальная       |
| Белая рубашка (снова!)    | Дубликат! (Ещё один) |
| Красная шляпа             | Оригинальная       |

Применим это в нашем коде:

Java
Скопировать код
List<String> clothes = Arrays.asList("Blue Shirt", "White Shirt", "Blue Pants", "White Shirt", "Red Hat");
Set<String> uniqueClothes = new HashSet<>();
List<String> duplicates = clothes.stream()
  .filter(n -> !uniqueClothes.add(n))
  .collect(Collectors.toList());

Как и с вешалкой, дубликаты в коде обнаруживаются мгновенно.

Стремление к идеалу с помощью обобщенных методов

Умелый программист использует обобщенные методы, которые работают с любым типом List:

Java
Скопировать код
public static <T> Set<T> findDuplicates(List<T> list) {
    Set<T> duplicates = new LinkedHashSet<>();
    Set<T> uniques = new HashSet<>();

    for (T t : list) {
        if (!uniques.add(t)) {
            duplicates.add(t);
        }
    }
    return duplicates;
}

Этот метод справляется с любыми типами списков и возвращает Set дубликатов, при этом сохраняя порядок их добавления.

Производительность против сложности: выбор непрост

Выбор между производительностью и сложностью похож на нелегкий принципиальный выбор. Запомните:

  • Sets и Streams улучшают производительность.
  • Сложность может увеличиваться при минимизации кода.
  • Стремимся к достижению удобству в читаемости и простоте в коде.

Превращение в Set для окончательного отделения уникальных

Для полного избавления от дубликатов, превратите List в Set:

Java
Скопировать код
Set<Integer> duplicatesSet = new HashSet<>(duplicates);

Как феникс воскресает из пепла, так и наш List, преобразовавшись в Set, оставляет за собой только уникальные элементы.