предисловие
Java8
Предоставленный интерфейс Stream упрощает потоковую передачу и функциональное программирование.
Теперь некоторые коллекции часто обрабатываются с помощью Stream, что улучшает читаемость кода по сравнению с циклами. Если вы пойдете дальше, повторно используйтеReactor
Реактивное программирование принесет больше преимуществ, таких как обработка исключений, управление потоком выполнения, параллелизм, буферизация и т. д., декларативно выполняет многие функции, которые могут быть выполнены только с помощью императивного программирования и многих кодов.
Сцены
В процессе сбора с помощью Stream один раз используйтеCollectors.groupingBy
иCollectors.reducing
,что-то не так. Запишите это здесь.
Сцена — это набор бизнес-объектов.Foo
:
@Getter
@Setter
@ToString
class Foo{
// 分组属性
private String name;
// 需求:分组平均值
private Integer m;
// 需求:分组平均值
private Integer n;
// 非业务属性,计算平均值时使用
private Integer count;
public Foo(String name, Integer m, Integer n) {
this.name = name;
this.m = m;
this.n = n;
}
}
Автор хочетStream<Foo>
Для этого потока сначала соберите его в группах по имени, а затем подсчитайте среднее значение атрибутов M и N для каждой группы и, наконец, вернутьFoo
Было написано так:
Foo f1 = new Foo("l", 1, 2),
f2 = new Foo("l", 1, 2),
f3 = new Foo("l", 1, 2),
f4 = new Foo("c", 1, 2),
f5 = new Foo("c", 1, 2),
f6 = new Foo("q", 1, 2),
f7 = new Foo("q", 1, 2);
System.out.println(Stream.of(f1, f2, f3, f4, f5, f6, f7)
.collect(Collectors.groupingBy(
Foo::getName,
Collectors.reducing(new Foo(null, 0, 0), (o, p) -> {
if (o.getName() == null) {
o.setName(p.getName());
}
o.setM(o.getM() + p.getM());
o.setN(o.getN() + p.getN());
o.setCount(o.getCount() == null ? 1 : o.getCount() + 1);
return o;
})
))
);
Окончательный результат:
{q=Foo(name=l, m=7, n=14, count=6), c=Foo(name=l, m=7, n=14, count=6), l=Foo(name=l, m=7, n=14, count=6)}
сгруппировано по имениFoo
Все одно, код не такой, как ожидалось, во-первыхgroupingBy
группы, а затем действуйте в соответствии с каждой группойreducing
собирай, но ставь оригиналStream
Полная коллекция как объект дает каждой группе значение.
Причины и решения
Проверил StackOverflow и прочитал егоCollectors.reducing
Исходный код:
public static <T> Collector<T, ?, T> reducing(T identity, BinaryOperator<T> op) {
return new CollectorImpl<>(
// 作为accumulator的函数式接口,看下段
boxSupplier(identity),
(a, t) -> { a[0] = op.apply(a[0], t); },
(a, b) -> { a[0] = op.apply(a[0], b[0]); return a; },
a -> a[0],
CH_NOID);
}
// 重点,每次只返回传进来的对象,而不是新创建一个
private static <T> Supplier<T[]> boxSupplier(T identity) {
return () -> (T[]) new Object[] { identity };
}
Нашел,Collectors.reducing
созданныйaccumulator
То, что возвращается, является переданным фиксированным объектом, а не потому, что восходящий потокCollectors.groupingBy
Просто создайте новый, а затем сгруппируйте, так чтолучше не модифицироватьCollectors.reducing
параметры.
Последнее требование можно решить следующим образом:
System.out.println(Stream.of(f1, f2, f3, f4, f5, f6, f7)
.collect(Collectors.groupingBy(
Foo::getName,
// 新构建Collector,每组accumulator会新创建Foo
Collector.of(() -> new Foo(null, 0, 0), (o, p) -> {
if (o.getName() == null) {
o.setName(p.getName());
}
o.setM(o.getM() + p.getM());
o.setN(o.getN() + p.getN());
o.setCount(o.getCount() == null ? 1 : o.getCount() + 1);
}, (o, p) -> o)
))
);
который выводит желаемый результат:
{q=Foo(name=q, m=2, n=4, count=2), c=Foo(name=c, m=2, n=4, count=2), l=Foo(name=l, m=3, n=6, count=3)}
Ссылаться на
stackoverflow.com/questions/4…
JDK11.0.2