Оптимальный поиск дубликатов в списке чисел 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
, оставляет за собой только уникальные элементы.