自定义Map收集器

ozxc1zmp  于 2021-07-13  发布在  Java
关注(0)|答案(1)|浏览(302)

我收集了一些 Map<Pair<DateTime, String>, List<Entity>> 以前使用流对其进行分组。 Entity 是一个简单的类 int 财产和 getValue() 方法。
现在,我想把 Entity 使用我的简单 EntityAccumulator 将上一个Map的类型修改为 Map<Pair<DateTime, String>, EntityAccumulator> . 就我所知,实现这一目标的唯一方法是创建自己的定制收集器,尽管我一直在努力 finisher() 应该返回的方法 Pair .
或者,有没有更简单的方法来达到我想要的结果?
流处理

Map<Pair<DateTime, String>, EntityAccumulator> collect = entities.stream()
                .collect(Collectors.groupingBy(entity-> Pair.of(entity.getTimestamp(), entity.getName())))
                .entrySet().stream()
                .collect(new EntityCollector()));

实体累加器

private static class EntityAccumulator {

        private int result = 0.0;

        public EntityAccumulator() { }

        public EntityAccumulator(int result) {
            this.result = result;
        }

        public void calculate(Entity entity) {
            result += entity.getValue();
        }

        public EntityAccumulatoradd(EntityAccumulator other) {
            return new EntityAccumulator(this.result + other.result);
        }
}

收藏家

public class EntityCollector implements Collector<Map.Entry<Pair<DateTime, String>, List<Entity>>, EntityAccumulator, Map.Entry<Pair<DateTime, String>, EntityAccumulator>> {

    @Override
    public Supplier<EntityAccumulator> supplier() {
        return EntityAccumulator::new;
    }

    @Override
    public BiConsumer<EntityAccumulator, Map.Entry<Pair<DateTime, String>, List<Entity>>> accumulator() {
        return (result, pairListEntry) -> pairListEntry.getValue().forEach(result::calculate);
    }

    @Override
    public BinaryOperator<EntityAccumulator> combiner() {
        return EntityAccumulator::add;
    }

    @Override
    public Function<EntityAccumulator, Map.Entry<Pair<DateTime, String>, EntityAccumulator>> finisher() {
        return (k) -> {
            return  null; // ??? HELP HERE 
        }
    }

    @Override
    public Set<Characteristics> characteristics() {
        return EnumSet.of(Characteristics.UNORDERED);
    }
}
mfuanj7w

mfuanj7w1#

显然,你真的想

Map<Pair<DateTime, String>, Double> collect = entities.stream()
    .collect(Collectors.groupingBy(
        entity -> Pair.of(entity.getTimestamp(), entity.getName()),
        Collectors.summingDouble(Entity::getValue)));

Map<Pair<DateTime, String>, Integer> collect = entities.stream()
    .collect(Collectors.groupingBy(
        entity -> Pair.of(entity.getTimestamp(), entity.getName()),
        Collectors.summingInt(Entity::getValue)));

取决于实际值类型。你的宣言 int result = 0.0 不太清楚。
首先,如果要对组执行缩减,应该提供 Collector 将值作为 groupingBy 收藏家。那么,这两个问题都不必解决, Map 也不是 Map.Entry .
因为它基本上是将实体折叠为一个数字(对于每个组),所以可以使用现有的收集器,即。 summingInt 或者 summingDouble .
创建自己的收集器时,不能在收集器函数中删除的finisher函数中重新生成信息。如果您的容器类型 EntityAccumulator 只包含一个数字,无法生成 Map.Entry<Pair<DateTime, String>, EntityAccumulator> 从它那里。
顺便说一下,您很少需要实现 Collector 与类的接口,即使在创建自定义收集器时也是如此。你可以简单地使用 Collector.of ,指定函数和特性,以创建 Collector .
所以用你原来的 EntityAccumulator 类别(假设, result 应该是 int 以及 0.0 你可以用

Map<Pair<DateTime, String>, Integer> collect = entities.stream()
    .collect(Collectors.groupingBy(
        entity -> Pair.of(entity.getTimestamp(), entity.getName()),
        Collector.of(EntityAccumulator::new,
                     EntityAccumulator::calculate,
                     EntityAccumulator::add,
                     ea -> ea.result,
                     Collector.Characteristics.UNORDERED)));

达到上述目的。也可以分两步执行操作,就像您尝试使用

Map<Pair<DateTime, String>, Integer> collect = entities.stream()
    .collect(Collectors.groupingBy(e -> Pair.of(e.getTimestamp(), e.getName())))
    .entrySet().stream()
    .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().stream().collect(
        Collector.of(EntityAccumulator::new,
                     EntityAccumulator::calculate,
                     EntityAccumulator::add,
                     ea -> ea.result,
                     Collector.Characteristics.UNORDERED))));

当然,这只是为了完整。这个答案开头显示的解决方案更简单、更有效。

相关问题