Сигнатуры методов groupingBy
и mapping
не имеют различий в отношении типа результата нижестоящего коллектора.Следовательно, вы получите тип результата List<T>
коллектора toList()
.В отличие от этого, toCollection
имеет дисперсию в параметре типа C extends Collection<T>
, но даже без него не будет проблем с присвоением ArrayList::new
для Supplier<Collection<…>>
:
public static Map<String, Collection<String>> convert(Collection<Foo> foos) {
return foos == null? new HashMap<>(): foos.stream().collect(
Collectors.groupingBy(f -> f.getSomeEnum().name(),
Collectors.mapping(Foo::getVal, Collectors.toCollection(ArrayList::new))));
}
Это точнотакой же как toList()
, учитывая текущую реализацию.Но это не принесет пользы от будущих улучшений, специально сделанных для коллектора toList()
.Альтернативой может быть продолжение использования toList()
, но преобразование типа в цепочку:
public static Map<String, Collection<String>> convert(Collection<Foo> foos) {
return foos == null? new HashMap<>(): foos.stream().collect(
Collectors.groupingBy(f -> f.getSomeEnum().name(),
Collectors.collectingAndThen(
Collectors.mapping(Foo::getVal, Collectors.toList()),
c -> c)));
}
Поскольку это расширяющее преобразование, функция преобразования проста, как c -> c
.К сожалению, базовая реализация не знает о тривиальности этой функции и будет перебирать значения карты результата, чтобы заменить каждое из них результатом применения этой функции.
Это можно решить с помощью специального расширенияcollector:
public static <T,A,R extends W,W> Collector<T,A,W> wideningResult(Collector<T,A,R> original) {
return Collector.of(original.supplier(), original.accumulator(), original.combiner(),
original.finisher().andThen(t -> t),
original.characteristics().toArray(new Collector.Characteristics[0]));
}
По сути, это то же самое, что и Collectors.collectingAndThen(original, t -> t)
, объединяя тривиальную расширяющую функцию преобразования.Но он сохраняет характеристики исходного коллектора, поэтому, если исходный коллектор имеет характеристику IDENTITY_FINISH
, у нас все еще будет ее, что позволяет пропустить завершающую операцию, которая в случае groupingBy
подразумевает, что ей не нужно повторятьнад картой, чтобы применить функцию.
Применение ее к фактическому варианту использования дает
public static Map<String, Collection<String>> convert(Collection<Foo> foos) {
return foos == null? new HashMap<>(): foos.stream().collect(
Collectors.groupingBy(f -> f.getSomeEnum().name(),
wideningResult(Collectors.mapping(Foo::getVal, Collectors.toList()))));
}