`Collectors.reduction` и `Collectors.groupingBy` `Stream`

Java

предисловие

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