Java 8 Stream List<Foo> 到 Map<Date, Map<String,Long>> 与条件 groupingBy

标签 java java-8 java-stream

下课:

public class Foo {
    private Date date;
    private String name;
    private Long number;
}

我现在有一个 List<Foo>我想将其转换为 Map<Date, Map<String,Long>> ( Long 应该是 numbers 的总和)。使这变得困难的是我想要内部 map 中恰好有 26 个条目,其中第 26 个称为“其他”,它总结了所有数字低于其他 25 个的东西。

我想出了以下代码:

data.stream().collect(Collectors.groupingBy(e -> e.getDate(), Collectors.groupingBy(e -> {
    if (/*get current size of inner map*/>= 25) {
        return e.getName();
    } else {
        return "Other";
    }

}, Collectors.summingLong(e -> e.getNumber()))));

如您所见,我不知道如何检查内部 map 中已有的元素数量。我怎样才能得到内部 map 的当前大小,或者有其他方法来实现我想要的吗?

我的 Java 7 代码:

Map<Date, Map<String, Long>> result = new LinkedHashMap<Date, Map<String, Long>>();
for (Foo fr : data) {
    if (result.get(fr.getDate()) == null) {
        result.put(fr.getDate(), new LinkedHashMap<String, Long>());
    }
    if (result.get(fr.getDate()) != null) {
        if (result.get(fr.getDate()).size() >= 25) {
            if (result.get(fr.getDate()).get("Other") == null) {
                result.get(fr.getDate()).put("Other", 0l);
            }
            if (result.get(fr.getDate()).get("Other") != null) {
                long numbers= result.get(fr.getDate()).get("Other");
                result.get(fr.getDate()).replace("Other", numbers+ fr.getNumbers());
            }
        } else {
            result.get(fr.getDate()).put(fr.getName(), fr.getNumbers());
        }
    }
}

编辑:

map 应该可以帮助我实现这样的表格:

enter image description here

但我需要先对“其他”求和。


如果您需要更多信息,请随时询问

最佳答案

我不认为这个操作会从使用 Stream API 中获益。不过,您可以使用 Java 8 功能改进操作:

Map<Date, Map<String, Long>> result = new LinkedHashMap<>();
for(Foo fr : data) {
    Map<String, Long> inner
      = result.computeIfAbsent(fr.getDate(), date -> new LinkedHashMap<>());
    inner.merge(inner.size()>=25?"Other":fr.getAirlineName(), fr.getNumbers(), Long::sum);
}

此代码假定每个日期的航空公司名称都是唯一的。否则,您必须将代码扩展为

Map<Date, Map<String, Long>> result = new LinkedHashMap<>();
for(Foo fr : data) {
    Map<String, Long> inner
      = result.computeIfAbsent(fr.getDate(), date -> new LinkedHashMap<>());
    inner.merge(inner.size() >= 25 && !inner.containsKey(fr.getAirlineName())?
      "Other": fr.getAirlineName(), fr.getNumbers(), Long::sum);
}

为航空公司正确积累值(value)。


为了完整起见,下面是如何将其实现为流操作。

由于自定义收集器具有一定的复杂性,因此值得将其编写为可重用代码:

public static <T,K,V> Collector<T,?,Map<K,V>> toMapWithLimit(
    Function<? super T, ? extends K> key, Function<? super T, ? extends V> value,
    int limit, K fallBack, BinaryOperator<V> merger) {

    return Collector.of(LinkedHashMap::new, (map, t) ->
            mergeWithLimit(map, key.apply(t), value.apply(t), limit, fallBack, merger),
            (map1,map2) -> {
                if(map1.isEmpty()) return map2;
                if(map1.size()+map2.size() < limit)
                    map2.forEach((k,v) -> map1.merge(k, v, merger));
                else
                    map2.forEach((k,v) ->
                        mergeWithLimit(map1, k, v, limit, fallBack, merger));
                return map1;
            });
}
private static <T,K,V> void mergeWithLimit(Map<K,V> map, K key, V value,
    int limit, K fallBack, BinaryOperator<V> merger) {
    map.merge(map.size() >= limit && !map.containsKey(key)? fallBack: key, value, merger);
}

这类似于 Collectors.toMap,但支持附加条目的限制和回退键。您可能认识到 Map.merge 调用,类似于循环解决方案的关键元素。

然后,您可以将收集器用作

Map<Date, Map<String, Long>> result = data.stream().collect(
    Collectors.groupingBy(Foo::getDate, LinkedHashMap::new,
        toMapWithLimit(Foo::getAirlineName, Foo::getNumbers, 25, "Other", Long::sum)));

关于Java 8 Stream List<Foo> 到 Map<Date, Map<String,Long>> 与条件 groupingBy,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53101661/

相关文章:

java - 如何在一个表达式中比较可为空的字符串和多个值?

java - 如何让 ScrollPane 适合 MasonryPane 的所有内容

java - 从给定 URL 的 YouTube 视频中检索详细信息

java - 当我不知道它是否预先是 lambda 时,如何安全地代理 lambda?

java - java for循环中的分支预测

java - Solr 8.3.1 : could not find or load org. apache.solr.util.SolrCLI

java stream - 字符串拆分两次后得到结果

java - Android topoche 可搜索微调器无法转换错误

java - Mockito spy 返回与实际方法调用不同的结果

java - 使用 java 流将评级列表转换为平均评级列表