当前位置: 首页 > 知识库问答 >
问题:

如何连接两个Java流,同时为不包含重复项的项指定特定逻辑?

闾丘淇
2023-03-14

我有两张地图使用同一个对象作为关键点。我想按键合并这两个流。当两个映射中都存在一个键时,我希望生成的映射运行一个公式。当一个键存在于单个映射中时,我希望该值为0。

Map<MyKey, Integer> map1;
Map<MyKey, Integer> map2;

<Map<MyKey, Double> result =
    Stream.concat(map1.entrySet().stream(), map2.entrySet().stream())
        .collect(Collectors.toMap(
            Map.Entry::getKey, Map.Entry::getValue,
            (val1, val2) -> (val1 / (double)val2) * 12D));

如果键存在于两个映射中,这将使用公式,但我需要一个简单的方法来将仅存在于两个映射之一中的键的值设置为0D。

我可以通过做集合数学,尝试计算两个键集的内部连接,然后从它们的完全外部连接中减去内部连接结果来实现这一点。。。但这是很多感觉不必要的工作。

有没有更好的方法,或者我可以使用流式应用编程接口轻松完成的事情?

共有3个答案

苏高旻
2023-03-14

Stream.concat在这里不是正确的方法,因为您将两个地图的元素扔在一起,因此需要在之后将它们分开。

您可以通过直接执行处理键的交集的预期任务来简化这一点,方法是应用您的函数并以不同的方式处理其他键。例如,当您流式传输一个地图而不是两个地图的连接时,您只需检查另一个地图中是否存在应用函数或使用零。然后,只存在于第二个映射中的键需要在第二个步骤中被归零:

Map<MyKey, Double> result = map1.entrySet().stream()
    .collect(Collectors.collectingAndThen(
        Collectors.toMap(Map.Entry::getKey, e -> {
            Integer val2 = map2.get(e.getKey());
            return val2==null? 0.0: e.getValue()*12.0/val2;
        }),
        m -> {
            Map<MyKey, Double> rMap = m.getClass()==HashMap.class? m: new HashMap<>(m);
            map2.keySet().forEach(key -> rMap.putIfAbsent(key, 0.0));
            return rMap;
        }));

这显然是因为流不能提供方便的方法来处理地图条目。此外,我们还必须在第二个处理步骤中处理未指定的映射类型。如果我们提供了一个地图供应商,我们还必须提供一个合并函数,使代码更加冗长。

更简单的解决方案是使用集合API而不是流API:

Map<MyKey, Double> result = new HashMap<>(Math.max(map1.size(),map2.size()));
map2.forEach((key, value) -> result.put(key, map1.getOrDefault(key, 0)*12D/value));
map1.keySet().forEach(key -> result.putIfAbsent(key, 0.0));

这显然不那么冗长,而且可能更有效,因为它省略了Stream解决方案的一些处理步骤,并为映射提供了正确的初始容量。它利用了这样一个事实,即如果我们在第一个映射的缺省值中使用零作为缺省值,则公式的计算结果为所需的零。如果您想使用不具有此属性的不同公式,或者想避免计算缺失映射,则必须使用

Map<MyKey, Double> result = new HashMap<>(Math.max(map1.size(),map2.size()));
map2.forEach((key, value2) -> {
    Integer value1 = map1.get(key);
    result.put(key, value1 != null? value1*12D/value2: 0.0);
});
map1.keySet().forEach(key -> result.putIfAbsent(key, 0.0));
施俊哲
2023-03-14

为了使这个解决方案工作,您的初始映射应该是Map

你甚至不需要流!您只需使用Map#replaceAll即可修改Map中的一个:

map1.replaceAll((k, v) -> map2.containsKey(k) ? 12D * v / map2.get(k) : 0D);

现在,您只需要将每个键添加到map2中的map1,而不是map1

map2.forEach((k, v) -> map1.putIfAbsent(k, 0D));

如果您不想修改Maps中的任何一个,那么您应该首先创建map1的深度副本。

高吉星
2023-03-14

这里有一个简单的方法,只流式处理键,然后查找值,并保持原始映射不变。

Map<String, Double> result =
    Stream.concat(map1.keySet().stream(), map2.keySet().stream())
          .distinct()
          .collect(Collectors.toMap(k -> k, k -> map1.containsKey(k) && map2.containsKey(k)
                                                 ? map1.get(k) * 12d / map2.get(k) : 0d));

测试

Map<String, Integer> map1 = new HashMap<>();
Map<String, Integer> map2 = new HashMap<>();
map1.put("A", 1);
map1.put("B", 2);
map2.put("A", 3);
map2.put("C", 4);
// code above here
result.entrySet().forEach(System.out::println);

输出

A=4.0
B=0.0
C=0.0
 类似资料:
  • 问题内容: 我正在为Java中的ERP系统进行定制。在我的定制中,我想使用Apache POI 3.10.1。因此,我集成了jars poi-3.10.1-20140818.jar和poi-ooxml-3.10.1-20140818.jar。 但是,这些jar包含几个类,这些类已经包含在ERP系统的核心代码中,但是有所不同。 如果核心ERP类覆盖POI类,则定制将引发运行时异常。如果POI类覆盖核

  • 问题内容: a = [1,1,1,2,3,4,4] >>> b = [1,1,2,3,3,3,4] 请注意,这不是一个相同的问题: 两个列表的Python交集保持重复 因为即使列表a中有三个1,列表b中也只有两个,所以结果应该只有两个。 问题答案: 您可以使用此方法,当您使用交叉路口时,它将为每个元素提供在任一列表中找到的最低计数。 输出 :

  • 本文向大家介绍Python中包含重复项的两个列表的区别,包括了Python中包含重复项的两个列表的区别的使用技巧和注意事项,需要的朋友参考一下 有时我们需要找出两个列表之间的差异。这还将意味着数学减法,其中如果第二列表中存在元素,则将其从第一列表中删除。重复项将保留。以下是我们可以实现此目标的方法。 我们可以使用collections模块中的Counter方法,该方法将跟踪元素的计数。直接的数学减

  • 我有2个Java项目,和。 是的依赖项。 最初包含一个,例如: 问题是,我现在需要将向下移动到中,但我无法移动向下进入,它必须保留在中。 什么是一个优雅的方法来重构这个到?

  • 我正在使用gradle构建一个groovy/java应用程序。在我向google guice 3.0添加依赖项之前,这一直很好。Gradle不会将guice jar添加到编译类路径中,至少看起来是这样。我遇到了这样的错误: 在我的身材中。gradle file I具有以下依赖项: 我正在使用Springsource工具套件2.9.2及其gradle插件开发应用程序,它使用gradles依赖关系管理

  • 问题内容: 我要添加到大型Java应用程序中的模块必须与另一家公司的SSL安全网站进行对话。问题是该站点使用自签名证书。我有证书的副本以验证我没有遇到中间人攻击,并且我需要将此证书合并到我们的代码中,以确保成功连接到服务器。 这是基本代码: 如果没有对自签名证书进行任何其他处理,它将死于conn.getOutputStream(),但以下情况除外: 理想情况下,我的代码需要教会Java在应用程序中