TLDR:
Чтобы собрать Map
, содержащий одно значение по ключу (Map<MyKey,MyObject>
), используйте Collectors.toMap()
.
Чтобы собрать в Map
несколько значений по ключу (Map<MyKey, List<MyObject>>
), используйте Collectors.groupingBy()
а>.
Коллекторы.toMap()
Написав:
chargePoints.stream().collect(Collectors.toMap(Point::getParentId, c -> c));
Возвращенный объект будет иметь тип Map<Long,Point>
.
Посмотрите на функцию Collectors.toMap()
, которую вы используете:
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper)
Он возвращает Collector
с результатом Map<K,U>
, где K
и U
— это тип возврата двух функций, переданных в метод. В вашем случае Point::getParentId
— это Long, а c
относится к Point
. Принимая во внимание, что Map<Long,Point>
возвращается, когда применяется collect()
.
И такое поведение скорее ожидается, поскольку Collectors.toMap() в javadoc указано:
возвращает Collector
, который накапливает элементы в Map
, чьи ключи и значения являются результатом применения предоставленных функций отображения к входным элементам.
Но если сопоставленные ключи содержат дубликаты (согласно Object.equals(Object)
), выдается IllegalStateException
Вероятно, это будет ваш случай, поскольку вы группируете Point
в соответствии с определенным свойством: parentId
.
Если сопоставленные ключи могут иметь дубликаты, вы можете использовать toMap(Function, Function, BinaryOperator)
перегружает, но на самом деле это не решит вашу проблему, так как не будет группировать элементы с тот же parentId
. Это просто даст возможность не иметь двух элементов с одним и тем же parentId
.
Коллекторы.groupingBy()
Чтобы выполнить ваше требование, вы должны использовать Collectors.groupingBy()
, поведение и объявление метода которого лучше подходят для ваших нужд:
public static <T, K> Collector<T, ?, Map<K, List<T>>>
groupingBy(Function<? super T, ? extends K> classifier)
Он указывается как:
Возвращает Collector, реализующий операцию «группировать по» для входных элементов типа T, группируя элементы в соответствии с функцией классификации и возвращая результаты в Map.
Метод принимает Function
.
В вашем случае параметр Function
равен Point
(type
Stream), и вы возвращаете Point.getParentId()
, так как хотите сгруппировать элементы по parentId
значениям.
Итак, вы могли бы написать:
Map<Long, List<Point>> pointByParentId =
chargePoints.stream()
.collect(Collectors.groupingBy( p -> p.getParentId()));
Или со ссылкой на метод:
Map<Long, List<Point>> pointByParentId =
chargePoints.stream()
.collect(Collectors.groupingBy(Point::getParentId));
Collectors.groupingBy(): идем дальше
Действительно, сборщик groupingBy()
идет дальше, чем реальный пример. Наконец, метод Collectors.groupingBy(Function<? super T, ? extends K> classifier)
— это просто удобный метод для хранения значений собранных Map
в List
.
Чтобы сохранить значения Map
в чем-то другом, кроме List
, или для сохранения результата определенного вычисления, groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream)
должен вас заинтересовать. .
Например :
Map<Long, Set<Point>> pointByParentId =
chargePoints.stream()
.collect(Collectors.groupingBy(Point::getParentId, toSet()));
Таким образом, помимо заданного вопроса, вы должны рассматривать groupingBy()
как гибкий способ выбора значений, которые вы хотите сохранить в собранном Map
, чем окончательно toMap()
не является.
person
davidxxx
schedule
21.07.2017
Collectors.groupingBy
. - person Louis Wasserman   schedule 21.07.2017