是否允许Collectors.toMap的mergeFunction修改其参数?

时间:2016-01-27 04:20:26

标签: lambda java-8 collectors

我有一组嵌套地图Stream<Map<String, Map<String, String>>>,我希望通过转换为入口集流并调用Collectors.toMap(...)来组合(使用外键;假设内部键是唯一的)。为了确保正确组合具有重复外键的地图,我将传递以下BinaryOperatortoMap(...)函数:

(existingMap, newMap) -> {
    existingMap.putAll(newMap);
    return existingMap;
}

代码似乎暂时有效,但我觉得我没有按预期使用Collectors.toMap(...),因为我正在改变累加器和组合器中的值。

以下是完整的代码段:

mapsToCombine.flatMap(map -> map.entrySet().stream()).collect(Collectors.toMap(Entry::getKey, Entry::getValue, (existingMap, newMap) -> {
    existingMap.putAll(newMap);
    return existingMap;
}));

2 个答案:

答案 0 :(得分:3)

您应该知道您正在修改的地图与源流中包含的地图完全相同,因此如果您的流是从数据结构(例如集合)构建的,则此数据结构将在以后以不可预测的方式进行修改。操作。它还意味着如果源包含多次相同的映射实例,则整个操作可能会中断(这将是违反非干扰规则的情况)。或者,如果源映射是不可变的。更糟糕的是,它可能会多次运行而没有问题并突然中断,在调试过程中可能无法重现。

通常,如果此输入是在流操作期间创建的结果,则通过修改其中一个输入进行合并可以正常工作。由收藏家本身。您可以使用Entry::getValue替换e -> new HashMap<>(e.getValue())函数轻松实现此目的。然后,保证合并操作的不干涉和地图的可变性,但是它会创建更多的临时地图,而不是在合并功能保存中创建地图。

或者,您可以使用groupingBy,它允许您为值指定收集器:

Map<String, Map<String, String>> result
  = mapsToCombine.flatMap(map -> map.entrySet().stream())
    .collect(Collectors.groupingBy(Entry::getKey, Collector.of(HashMap::new,
     (m,e) -> m.putAll(e.getValue()), (m1,m2) -> { m1.putAll(m2); return m1;})));

这不会修改任何源地图,但只创建一个可变结果地图,因此您可以在合并时加入它。

答案 1 :(得分:2)

似乎未明确指定,但在目前的实施中,这样做是完全安全的。