java - 自定义 map 收集器

标签 java dictionary java-8 java-stream collectors

我有一个由 Map<Pair<DateTime, String>, List<Entity>> 组成的集合之前使用流进行分组。 Entity是一个带有 int 的简单类属性(property)和getValue()方法。

现在,我想聚合 Entity 的值使用我的简单 EntityAccumulator将之前映射的类型修改为 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);
        }
}

Collection 家

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);
    }
}

最佳答案

显然,你实际上想做

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 collector 的第二个参数的值.然后,它不必处理两者,Map也不Map.Entry .

因为它基本上是将实体折叠成一个数字(对于每个组),您可以使用现有的收集器,即 summingInt summingDouble .

当您创建自己的收集器时,您无法在完成器函数中重新构造已放入累加器函数的信息。如果您的容器类型 EntityAccumulator仅包含一个数字,无法生成 Map.Entry<Pair<DateTime, String>, EntityAccumulator>从它。

顺便说一句,你很少需要实现 Collector与类的接口(interface),即使在创建自定义收集器时也是如此。您可以简单地使用 Collector.of , 指定功能和特性,创建一个 Collector .

所以使用你原来的 EntityAccumulator类(假设 result 应该是 int0.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))));

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

关于java - 自定义 map 收集器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41445979/

相关文章:

python - Pandas Dataframe 自动缩短字符串?

python - 在 Python 中递归添加字典

python - 应用 map 函数时在 python 中投影变量

java - 为什么 Collections.synchronizedList(list) 在内部使用 instanceof 检查?

java - 来自嵌套 POJO 的比较器

java - 尝试模拟 Elastic Search 的 RestHighLevelClient 时出现 NullPointerException 问题

java - context.remove(this) 引起的 RunTimeException

java - 使用 Eclipse Code Formatter 在其自己的行上配置枚举常量

一行中的 Java 8 流 min() 和 anyMatch() (或类似的东西)

java - 安卓工作室, "Unresolved Reference: activity_main"