予想通り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;
    }
}

今、私が持っているのコレクションFooのを、私はでグループ化したいと思います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;
        }
    }
}
rgettman:

まず、コンバイナ機能を使用すると、複数のスレッド(並列ストリーム)を使用していない場合に呼び出さ取得する必要はありません。コンバイナは、あなたのストリームのチャンクに操作の結果を結合するために呼び出されます。コンバイナを呼び出す必要はありませんので、何の並列処理は、ここではありません。

あなたはあなたのためにアキュムレータ関数のゼロ値を取得しています。表現

v += foo.v1 * foo.v2;

代わる v新しいとFloatオブジェクト。オリジナルアキュムレータオブジェクトが変更されません。それはまだです0fまた、Float他の数値ラッパー型(等String)不変であり、変更することはできません。

あなたは変更可能であるアキュムレータオブジェクトのいくつかの他の種類を必要とします。

class FloatAcc {
    private Float total;
    public FloatAcc(Float initial) {
        total = initial;
    }
    public void accumulate(Float item) {
        total += item;
    }
    public Float get() {
        return total;
    }
}

次に、あなたのカスタムを変更することができますCollector使用しますFloatAcc新規供給FloatAcc、通話accumulateaccumulatorの機能を、など

class FooCollector implements Collector<Foo, FloatAcc, Float> {
    @Override
    public Supplier<FloatAcc> supplier() {
        return () -> new FloatAcc(0f);
    }
    @Override
    public BiConsumer<FloatAcc, Foo> accumulator() {
        return (v, foo) -> v.accumulate(foo.v1 * foo.v2);
    }
    @Override
    public BinaryOperator<FloatAcc> combiner() {
        return (v1, v2) -> {
            v1.accumulate(v2.get());
            return v1;
        };
    }
    @Override
    public Function<FloatAcc, Float> finisher() {
        return FloatAcc::get;
    }
    @Override
    public Set<Characteristics> characteristics() {
        Set<Characteristics> characteristics = new TreeSet<>();
        return characteristics;
    }
}

これらの変更により、私はあなたが期待しているものを手に入れます:

{green=12.0, blue=10.0}

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=199726&siteId=1