下课:
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 应该可以帮助我实现这样的表格:
但我需要先对“其他”求和。
如果您需要更多信息,请随时询问
最佳答案
我不认为这个操作会从使用 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/