Оптимальный поиск дубликатов в списке чисел Java
Быстрый ответ
Для поиска дубликатов в List примените Set. Это поможет отсечь повторения. Вот как это осуществить с помощью операций stream:
Set<Integer> uniqueItems = new HashSet<>();
List<Integer> duplicates = list.stream()
.filter(n -> !uniqueItems.add(n))
.collect(Collectors.toList());
Также, элемент добавится в uniqueItems в случае его присутствия там (то есть, если это дубликат), метод возвратит false, и после посредством filter подобные элементы будут сохранены.

Понятность и лаконичность с потоками Java 8
API потоков Java 8 предоставляет изящный способ фильтрации данных и группировки их с использованием Collectors.groupingBy. Вот наш план:
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 приносит эффективность и высокую производительность:
Set<Integer> seen = ConcurrentHashMap.newKeySet();
List<Integer> duplicateNumbers = list.parallelStream()
.filter(n -> !seen.add(n))
.collect(Collectors.toList());
Применяется ConcurrentHashMap.newKeySet() для атомарных операций, что необходимо при работе с parallel stream, чтобы не упустить дубликаты.
Как справиться с кризисом идентичности: разбираемся с уникальными элементами и группами
Применяйте distinct и groupingBy для дифференциации элементов и выделения дубликатов:
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());
Мы создаем карту, отображающую частоту встречаемости элементов списка и отбираем только те, которые встречаются более одного раза.
Визуализация
Визуализируем код, приведя аналогию с вешалкой для одежды:
| Вешалка 🧥 | Статус |
| -------------------------- | ------------------ |
| Синяя рубашка | Оригинальная |
| Белая рубашка | Дубликат! (1-й) |
| Синие брюки | Оригинальная |
| Белая рубашка (снова!) | Дубликат! (Ещё один) |
| Красная шляпа | Оригинальная |
Применим это в нашем коде:
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:
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:
Set<Integer> duplicatesSet = new HashSet<>(duplicates);
Как феникс воскресает из пепла, так и наш List, преобразовавшись в Set, оставляет за собой только уникальные элементы.


