我有一个Map<Long, List<Member>>()
我希望生成一个Map<Member, Long>
,它是通过在List<Member>
中迭代Map.Entry<Long, List<Member>>
并为每个成员汇总每个地图条目的键来计算的在该成员名单中。没有非功能性的方式很容易,但是如果不使用Java 8 Stream API编写自定义收集器,我就无法找到方法。我想我需要像Stream.collect(Collectors.toFlatMap)
这样的东西,但Collectors
中没有这样的方法。
我能找到的最佳方式是:
longListOfMemberMap = new HashMap<Long, List<Member>>()
longListOfMemberMap.put(10, asList(member1, member2));
Map<Member, Long> collect = longListOfMemberMap.entrySet().stream()
.collect(new Collector<Map.Entry<Long, List<Member>>, Map<Member, Long>, Map<Member, Long>>() {
@Override
public Supplier<Map<Member, Long>> supplier() {
return HashMap::new;
}
@Override
public BiConsumer<Map<Member, Long>, Map.Entry<Long, List<Member>>> accumulator() {
return (memberLongMap, tokenRangeListEntry) -> tokenRangeListEntry.getValue().forEach(member -> {
memberLongMap.compute(member, new BiFunction<Member, Long, Long>() {
@Override
public Long apply(Member member, Long aLong) {
return (aLong == null ? 0 : aLong) + tokenRangeListEntry.getKey();
}
});
});
}
@Override
public BinaryOperator<Map<Member, Long>> combiner() {
return (memberLongMap, memberLongMap2) -> {
memberLongMap.forEach((member, value) -> memberLongMap2.compute(member, new BiFunction<Member, Long, Long>() {
@Override
public Long apply(Member member, Long aLong) {
return aLong + value;
}
}));
return memberLongMap2;
};
}
@Override
public Function<Map<Member, Long>, Map<Member, Long>> finisher() {
return memberLongMap -> memberLongMap;
}
@Override
public Set<Characteristics> characteristics() {
return EnumSet.of(Characteristics.UNORDERED);
}
});
// collect is equal to
// 1. member1 -> 10
// 2. member2 -> 10
示例中的代码采用Map&gt;作为参数并生成一个Map:
参数Map<Long, List<Member>>
:
// 1. 10 -> list(member1, member2)
收集的值Map<Member, Long>
:
// 1. member1 -> 10
// 2. member2 -> 10
然而,正如你所看到的那样,它比非功能性方式更难看。我尝试了Collectors.toMap
并减少了Stream
的方法,但我找不到使用几行代码的方法。
对于这个问题,哪种方式最简单,最实用?
答案 0 :(得分:6)
longListOfMemberMap.entrySet().stream()
.flatMap(entry -> entry.getValue().stream().map(
member ->
new AbstractMap.SimpleImmutableEntry<>(member, entry.getKey())))
.collect(Collectors.groupingBy(
Entry::getKey,
Collectors.summingLong(Entry::getValue)));
......虽然更简单但更有必要的替代方案可能看起来像
Map<Member, Long> result = new HashMap<>();
longListOfMemberMap.forEach((val, members) ->
members.forEach(member -> result.merge(member, val, Long::sum)));
答案 1 :(得分:0)
我只想指出,在依赖Collector.of
并将匿名类转换为lambdas时,可以更简洁地写下您发布的代码:
Map<Member, Long> result = longListOfMemberMap.entrySet().stream()
.collect(Collector.of(
HashMap::new,
(acc, item) -> item.getValue().forEach(member -> acc.compute(member,
(x, val) -> Optional.ofNullable(val).orElse(0L) + item.getKey())),
(m1, m2) -> {
m1.forEach((member, val1) -> m2.compute(member, (x, val2) -> val1 + val2));
return m2;
}
));
这仍然很麻烦,但至少不是压倒性的。