【问题标题】:Can we modify Map values while iterating in Java 8我们可以在 Java 8 中迭代时修改 Map 值吗
【发布时间】:2020-07-26 22:42:55
【问题描述】:

我有一个Map<Integer,String>。我需要能够先过滤掉偶数的键,然后迭代映射值并修改映射值的内容。我尝试运行下面的代码,但值没有改变。 java 8中是否可以在迭代Map时根据某些条件修改值?

Map<Integer,String> m = new HashMap<>();
    m.put(1,"ABC");
    m.put(2,"PQR");
    m.put(3,"XYZ");
    m.put(4,"RST");

Map<Integer,String> m1 =  m.entrySet().stream()
    .filter(map -> map.getKey().intValue() %2 == 0)
    .collect(Collectors.toMap(
        map -> map.getKey(),map -> map.getValue()));
            Map<Integer,String> m3 = m1.entrySet().stream()
                .filter(m2 -> {
                    if(m2.getValue().equalsIgnoreCase("PQR")){
                        m2.getValue().replace("PQR","PQR1");
                    } else if(m2.getValue().equalsIgnoreCase("RST")) {
                        m2.getValue().replace("RST", "RST1");
                    }
                    return true;
                }).collect(Collectors.toMap(m2 -> m2.getKey(), m2 -> m2.getValue()));

System.out.println(m3);

我得到的答案是{2=PQR, 4=RST},但我的答案应该是{2=PQR1, 4=RST1}

【问题讨论】:

    标签: java dictionary lambda java-8 java-stream


    【解决方案1】:

    问题来自replace方法:由于String是不可变的,该方法返回一个新字符串。但是代码忽略了这个返回值。

    为了对您的代码进行最小程度的调整,请像这样创建 m3 映射:

        Map<Integer,String> m3 = m1.entrySet().stream().map(m2 -> {
            if(m2.getValue().equalsIgnoreCase("PQR")){
                m2.setValue("PQR1");
            }else if(m2.getValue().equalsIgnoreCase("RST")) {
                m2.setValue("RST1");
            }
            return m2;
        }).collect(Collectors.toMap(m2 -> m2.getKey(), m2 -> m2.getValue()));
    

    【讨论】:

    • 感谢它工作得非常好,只是不需要返回 m2 需要返回 true
    • 不,因为我把你原来的filter改成了map。我认为更改 filter 方法中的值不是一个好主意。
    【解决方案2】:

    我得到的答案是 {2=PQR, 4=RST} 但我的答案应该是 {2=PQR1, 4=RST1}

    字符串是不可变的,您无需为条目设置新值。您必须创建一个新条目,然后将其收集到Map

    .map(m2 -> new AbstractMap.SimpleEntry<>(m2.getKey(), m2.getValue().replace("PQR","PQR1")))
    

    ...或使用简单的Map::setValue

    但是,有一个更好的方法。基本上,你想要:

    • 过滤这些具有特定value 的条目。
    • 更改这些值

    Stream API 被设计为与Collections 而非Maps 一起使用,但是,无论如何,它们都是基于集合构建的。有一个办法,有点笨拙,但是是:

    Map<String, String> helpMap = new HashMap<>();       // the replacement map
    helpMap.put("PQR", "PQR1");
    helpMap.put("RST", "RST1");
    
    Map<Integer, String> newMap = m.entrySet().stream()
        .filter(map -> map.getKey().intValue() %2 == 0) // only qualified keys
        .map(entry -> new AbstractMap.SimpleEntry<>(
                entry.getKey(),                         // key -> key
                replacementMap.getOrDefault(            // lookup the value in helpMap
                    entry.getValue(),                   // ... by the value as key
                    entry.getValue())))                 // ... or keep the original value
        .collect(Collectors.toMap(                      // collect back to Map<Integer,String>
                Map.Entry::getKey,                      // ... keys
                Map.Entry::getValue));                  // ... values
    

    注意事项:

    • 该解决方案基于将每个条目映射到具有修改值的新Map.Entry,从中使用正确的收集器可以轻松完成新的Map
    • 如果在helpMap 中找不到要替换的值,则使用原始值 - 这取决于您。您可以在.map(..) 之前使用.filter(entry -&gt; replacementMap.containsKey(entry.getValue())) 过滤掉这些无效值。

    【讨论】:

      【解决方案3】:

      您可以一次完成所有操作,过滤您想要更改的值并将其收集到新地图中,例如:

      Map<Integer, String> collect = m.entrySet().stream()
          .filter(e -> e.getKey() % 2 == 0)
          .peek(e -> {
              if (e.getValue().equalsIgnoreCase("PQR")) {
                  e.setValue("PQR1");
              }
              if (e.getValue().equalsIgnoreCase("RST")) {
                  e.setValue("RST1");
              }
          }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
      
      System.out.println(collect);
      

      【讨论】:

      • 这会修改原始地图。
      猜你喜欢
      • 2014-01-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-04
      • 2018-09-16
      • 2021-05-03
      • 2016-11-15
      相关资源
      最近更新 更多