Немного удивительно, что у вас возникают трудности, так как вы уже продемонстрировали знание всех необходимых вещей.Вы знаете, что groupingBy
может занять другое Collector
, вы назвали правильное, уже toMap
, и вы уже использовали функции для извлечения значений Map.Entry
.
Объединение этих вещей даетyou
Map<Long, Map<Integer, Long>> groups = filtered.entrySet().stream()
.collect(Collectors.groupingBy(x -> x.getValue(),
Collectors.toMap(x -> x.getKey(), x -> x.getValue())));
System.out.println("grouped " + groups);
Чтобы лучше продемонстрировать операцию, я изменил ввод на
List<Integer> list = Arrays.asList(1, 1, 1, 2, 3, 3, 3, 3, 4, 4, 4);
, что приводит к
grouped {3=[1=3, 4=3], 4=[3=4]}
, хотя нет смысла повторятьзначения, которые всегда совпадают с ключами внешних карт.Таким образом, альтернативой будет
Map<Long, List<Integer>> groups = filtered.entrySet().stream()
.collect(Collectors.groupingBy(Map.Entry::getValue,
Collectors.mapping(Map.Entry::getKey, Collectors.toList())));
System.out.println("grouped " + groups);
, что приводит к
grouped {3=[1, 4], 4=[3]}
Обратите внимание, что вы не должны использовать forEach
/ forEachOrdered
до put
на карте.Ваши промежуточные шаги скорее должны быть
//Sort desc
Map<Integer, Long> descendingSorted = countGrouped.entrySet().stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
(a,b) -> { throw new AssertionError(); }, LinkedHashMap::new));
System.out.println("sorted " + descendingSorted);
//filter
Map<Integer, Long> filtered = descendingSorted.entrySet().stream()
.filter(x -> x.getValue() >= 2)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
(a,b) -> { throw new AssertionError(); }, LinkedHashMap::new));
System.out.println("filtered " + filtered);
Сборщик toMap
, принимающий фабрику карт, вынуждает нас предоставлять функцию слияния, но так как наш ввод уже является картой, которая должна иметь различные ключи, я предоставил всегдаВызов функции здесь, так как что-то было бы серьезно неправильно, если бы обнаружились дубликаты.
Но обратите внимание, что принуждение всех этих операций собирать в новые карты является излишне сложным и неэффективным.Также нет смысла сначала сортировать данные целиком, а затем уменьшать объем данных с помощью filter
.Сначала фильтрация потенциально уменьшит работу этапа сортировки, тогда как результат операции фильтра не должен зависеть от порядка.
Гораздо лучше выполнить всю операцию в одном конвейере
List<Integer> list = Arrays.asList(1, 1, 1, 2, 3, 3, 3, 3, 4, 4, 4);
Map<Integer, Long> countGrouped = list.stream().collect(
Collectors.groupingBy(x -> x, Collectors.counting()));
System.out.println("group by value, count " + countGrouped);
Map<Long, List<Integer>> groups = countGrouped.entrySet().stream()
.filter(x -> x.getValue() >= 2)
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.collect(Collectors.groupingBy(Map.Entry::getValue, LinkedHashMap::new,
Collectors.mapping(Map.Entry::getKey, Collectors.toList())));
System.out.println("grouped " + groups);
Обратите внимание, что в отличие от предыдущего кода, теперь последняя операция группировки также будет сохранять порядок, что приводит к
grouped {4=[3], 3=[1, 4]}
, т. Е. Группы сортируются по убыванию количества.
Посколькуcount является ключом полученной карты, мы также можем использовать внутренне отсортированную карту в качестве типа результата и пропустить этап сортировки:
Map<Long, List<Integer>> groups = countGrouped.entrySet().stream()
.filter(x -> x.getValue() >= 2)
.collect(Collectors.groupingBy(Map.Entry::getValue,
() -> new TreeMap<>(Comparator.<Long>reverseOrder()),
Collectors.mapping(Map.Entry::getKey, Collectors.toList())));
Основное различие заключается в поведении карты результатов после потоковая операция, например, если вы добавите в нее больше элементов, так как TreeMap
вставит новые ключи в порядке убывания, тогда как LinkedHashMap
добавит их в конец, поддерживая порядок вставки.