如何将父map中的map值与java8流相结合

jw5wzhpr  于 2021-07-03  发布在  Java
关注(0)|答案(2)|浏览(254)

我在一张Map里面有一张Map,看起来像这样:

Map<String, Map<Integer, BigDecimal>> mapInMap; //--> with values like: 
/*
"a": (1: BigDecimal.ONE),
"b": (2: BigDecimal.TEN),
"c": (1: BigDecimal.ZERO)

* /

我希望通过期望得到以下结果来组合内部Map:

Map<Integer, BigDecimal> innerMapCombined; //--> with values:
/*
1: BigDecimal.ZERO,
2: BigDecimal.TEN

* /

这是我的解决方案,通过预定义组合Map并使用foreach:

Map<Integer, BigDecimal> combined = new HashMap<>();
mapInMap.forEach((str, innerMap) -> {
  innerMap.forEach(combined::putIfAbsent);
});

但这将忽略 (1: BigDecimal.ZERO) .
你能用Java8流提供一个单线解决方案吗?

jgzswidk

jgzswidk1#

问题是,只要初始化Map并在内部Map上添加重复的键,就会重写这些键,因为这些Map不接受重复的键。因此,您需要首先更改以下内容:

Map<String, Map<Integer, BigDecimal>> mapInMap;

Map 允许复制密钥,例如google guava的multimap:

Map<String, Multimap<Integer, BigDecimal>> mapInMap = new HashMap<>();

内部贴图的创建方式如下:

Multimap<Integer, BigDecimal> x1 = ArrayListMultimap.create();
 x1.put(1, BigDecimal.ONE);
 mapInMap.put("a", x1);

只有现在您才能尝试使用Java8StreamsAPI来解决您的问题。例如:

Map<Integer, BigDecimal> map = multiMap.values()
                                       .stream()
                                       .flatMap(map -> map.entries().stream())
                                       .collect(Collectors.toMap(Map.Entry::getKey,
                                                                 Map.Entry::getValue, 
                                                                 (v1, v2) -> v2));

使用的mergefunction参数解决了重复密钥冲突 toMap 方法。我们明确表示取第二个值 (v1, v2) -> v2 如果是重复的。

svmlkihl

svmlkihl2#

问题:
解决当前解决方案不起作用的原因是 Map#putIfAbsent 方法仅添加Map中已存在的值,而不替换该值。
解决方案用于: Map#put 是一种方法,但是它的局限性在于,您无法决定是否要始终保留此类键的第一个值、计算新值还是始终使用最后一个值。因此,我建议使用 Map#computeIfPresent 以及 Map#putIfAbsent 或者最好是一种同时完成所有任务的方法 Map#merge(K, V, BiFunction) 用一个 BiFunction remappingFunction :
remappingfunction—如果存在值,则重新计算该值的函数

Map<Integer, BigDecimal> resultMap = new HashMap<>();
for (Map<Integer, BigDecimal> map: mapInMap.values()) {
    for (Map.Entry<Integer, BigDecimal> entry: map.entrySet()) {
         resultMap.merge(entry.getKey(), entry.getValue(), (l, r) -> r);
    }
}

使用流api的解决方案:
在类似流的解决方案中重写它,方法是相同的。唯一的区别是streamapi的声明性语法,然而,其思想是非常相同的。
只需平面Map结构并收集到一个带有 Collector.toMap(Function, Function, BinaryOperator 使用 BinaryOperator mergeFunction 合并重复的键。
mergefunction—一个合并函数,用于解决与同一键相关的值之间的冲突,如提供给map.merge(object,object,bifunction)的那样

Map<Integer, BigDecimal> resultMap = mapInMap.values().stream()
    .flatMap(entries -> entries.entrySet().stream())
    .collect(Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue, (l, r) -> r));

注意:@dreamcrash在速度方面的出色的streamapi回答也值得称赞。
结果: {1=1, 2=10} 结果是当你弹出这样的Map(注意 BigDecimal 打印为数字)。此输出与预期输出匹配。

1=BigDecimal.ZERO
2=BigDecimal.TEN

请注意 Map#merge(K, V, BiFunction) 以及 Collector.toMap(Function, Function, BinaryOperator 使用非常相似的方法得到相同的结果。

相关问题