Collectors.groupingBy 的自定义收集器无法按预期工作

考虑简单的类Foo:


public class Foo {


    public Float v1;

    public Float v2;

    public String name;


    public Foo(String name, Float v1, Float v2) {

        this.name = name;

        this.v1 = v1;

        this.v2 = v2;

    }


    public String getName() {

        return name;

    }

}

现在,我有一个Foos 的集合,我想按 s 对它们进行分组Foo::getName。我写了一个自定义收集器来做到这一点,但它似乎没有按预期工作。更准确地说,combiner()永远不会被调用。为什么?


public class Main {


    public static void main(String[] args) {


        List<Foo> foos = new ArrayList<>();

        foos.add(new Foo("blue", 2f, 2f));

        foos.add(new Foo("blue", 2f, 3f));

        foos.add(new Foo("green", 3f, 4f));


        Map<String, Float> fooGroups = foos.stream().collect(Collectors.groupingBy(Foo::getName, new FooCollector()));

        System.out.println(fooGroups);

    }


    private static class FooCollector implements Collector<Foo, Float, Float> {


        @Override

        public Supplier<Float> supplier() {

            return () -> new Float(0);

        }


        @Override

        public BiConsumer<Float, Foo> accumulator() {

            return (v, foo) -> v += foo.v1 * foo.v2;

        }


        @Override

        public BinaryOperator<Float> combiner() {

            return (v1, v2) -> v1 + v2;

        }


        @Override

        public Function<Float, Float> finisher() {

            return Function.identity();

        }


        @Override

        public Set<Characteristics> characteristics() {

            Set<Characteristics> characteristics = new TreeSet<>();

            return characteristics;

        }

    }

}



翻阅古今
浏览 226回答 2
2回答

翻过高山走不出你

首先,如果您不使用多个线程(并行流),则不需要调用组合器函数。调用组合器以组合流块上的操作结果。这里没有并行性,因此不需要调用组合器。由于您的累加器功能,您得到零值。表达方式v += foo.v1 * foo.v2;将替换 v为新Float对象。原来的累加器对象没有被修改;它仍然是0f。此外,Float与其他数字包装器类型(和String)一样,它是不可变的,无法更改。您需要一些其他类型的可变累加器对象。class FloatAcc {&nbsp; &nbsp; private Float total;&nbsp; &nbsp; public FloatAcc(Float initial) {&nbsp; &nbsp; &nbsp; &nbsp; total = initial;&nbsp; &nbsp; }&nbsp; &nbsp; public void accumulate(Float item) {&nbsp; &nbsp; &nbsp; &nbsp; total += item;&nbsp; &nbsp; }&nbsp; &nbsp; public Float get() {&nbsp; &nbsp; &nbsp; &nbsp; return total;&nbsp; &nbsp; }}然后您可以修改您的自定义Collector以使用FloatAcc. 提供一个新的,FloatAcc调用函数等。accumulateaccumulatorclass FooCollector implements Collector<Foo, FloatAcc, Float> {&nbsp; &nbsp; @Override&nbsp; &nbsp; public Supplier<FloatAcc> supplier() {&nbsp; &nbsp; &nbsp; &nbsp; return () -> new FloatAcc(0f);&nbsp; &nbsp; }&nbsp; &nbsp; @Override&nbsp; &nbsp; public BiConsumer<FloatAcc, Foo> accumulator() {&nbsp; &nbsp; &nbsp; &nbsp; return (v, foo) -> v.accumulate(foo.v1 * foo.v2);&nbsp; &nbsp; }&nbsp; &nbsp; @Override&nbsp; &nbsp; public BinaryOperator<FloatAcc> combiner() {&nbsp; &nbsp; &nbsp; &nbsp; return (v1, v2) -> {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v1.accumulate(v2.get());&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return v1;&nbsp; &nbsp; &nbsp; &nbsp; };&nbsp; &nbsp; }&nbsp; &nbsp; @Override&nbsp; &nbsp; public Function<FloatAcc, Float> finisher() {&nbsp; &nbsp; &nbsp; &nbsp; return FloatAcc::get;&nbsp; &nbsp; }&nbsp; &nbsp; @Override&nbsp; &nbsp; public Set<Characteristics> characteristics() {&nbsp; &nbsp; &nbsp; &nbsp; Set<Characteristics> characteristics = new TreeSet<>();&nbsp; &nbsp; &nbsp; &nbsp; return characteristics;&nbsp; &nbsp; }}通过这些更改,我得到了您所期望的:{green=12.0, blue=10.0}

慕沐林林

您可以解释为什么电流收集器不能从rgettman工作。值得检查一下存在哪些帮助方法来创建自定义收集器。例如,整个收集器可以更简洁地定义为:reducing(0.f,&nbsp;v&nbsp;->&nbsp;v.v1&nbsp;*&nbsp;v.v2,&nbsp;(a,&nbsp;b)&nbsp;->&nbsp;a&nbsp;+&nbsp;b)并非总是可以使用这些方法;但是简洁性(并且可能是经过充分测试的)应该尽可能使它们成为首选。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java