【问题标题】:Processing a list of maps using Java 8 streams使用 Java 8 流处理地图列表
【发布时间】:2017-04-07 02:27:58
【问题描述】:

如何将此代码简化为单个 lambda 表达式?这个想法是有一个地图列表,我想使用键上的过滤器创建一个新的地图列表。在这个例子中,我想重新映射它,使它只保留键“x”和“z”。

    Map<String, String> m0 = new LinkedHashMap<>();
    m0.put("x", "123");
    m0.put("y", "456");
    m0.put("z", "789");

    Map<String, String> m1 = new LinkedHashMap<>();
    m1.put("x", "000");
    m1.put("y", "111");
    m1.put("z", "222");

    List<Map> l = new ArrayList<>(Arrays.asList(m0, m1));
    List<Map> tx = new ArrayList<>();
    for(Map<String, String> m : l) {
        Map<String, String> filtered = m.entrySet()
                .stream()
                .filter(map -> map.getKey().equals("x") || map.getKey().equals("z"))
                .collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue()));
        tx.add(filtered);
    }
    System.err.println("l: " + l);
    System.err.println("tx: " + tx);

输出:

    l: [{x=123, y=456, z=789}, {x=000, y=111, z=222}]
    tx: [{x=123, z=789}, {x=000, z=222}]

【问题讨论】:

    标签: java lambda java-stream


    【解决方案1】:

    当然,您可以将整个操作转换为一个 Stream 操作。

    // no need to copy a List (result of Array.asList) to an ArrayList, by the way
    List<Map<String, String>> l = Arrays.asList(m0, m1);
    
    List<Map<String, String>> tx = l.stream().map(m -> m.entrySet().stream()
            .filter(map -> map.getKey().equals("x") || map.getKey().equals("z"))
            .collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue())))
        .collect(Collectors.toList());
    

    但请注意,通过Map 进行流式传输和过滤是具有线性时间复杂度的操作,因为它会根据过滤器检查每个映射的每个键,而您想要的实际键数量很少保持。所以在这里,使用起来更简单、更高效(对于较大的地图)

    List<Map<String, String>> tx = l.stream()
        .map(m -> Stream.of("x", "y")
                        .filter(m::containsKey).collect(Collectors.toMap(key->key, m::get)))
        .collect(Collectors.toList());
    

    每个地图只会执行四次查找。如果它困扰您,您甚至可以将其减少到两次查找,但是,常量因素与整体时间复杂度无关,这将是常量时间,如果地图具有常量时间查找,例如 HashMap。即使对于具有O(log(n)) 查找时间复杂度的映射,例如TreeMap,如果映射大于示例代码的三个映射,这将比线性扫描更有效。

    【讨论】:

      【解决方案2】:

      你可以试试这样的:

      List<Map<String, String>> l = Arrays.asList(m0, m1);
      
      l.forEach(map -> {
          map.entrySet().removeIf(e -> !e.getKey().equals("x") && !e.getKey().equals("z"));
      });
      

      如果输入键不是xz,它只会删除每个Map&lt;String, String&gt; 中的所有映射。

      编辑:您应该使用 Radiodef 的等效但更短的方法!

      List<Map<String, String>> l = Arrays.asList(m0, m1);
      
      l.forEach(map -> map.keySet().retainAll(Arrays.asList("x", "z"));
      

      【讨论】:

      • 在这种风格中,更小的版本是使用类似map.keySet().retainAll(Arrays.asList("x", "z")) 的东西。 (编辑:但作为旁注,OP 与收集器的代码制作了一个副本,因此这些并不完全相同。)
      • 即使是基于 removeIf 的解决方案也将受益于表达式形式并首先使用 .keySet() 而不是在 Entry 上调用 .getKey() 两次:l.forEach(map -&gt; map.keySet().removeIf(k -&gt; !k.equals("x") &amp;&amp; !k.equals("z")));
      【解决方案3】:

      试试下面的代码(我为desiredKeys声明了一个列表):

      public class Main {
          public static void main(String[] args) {
              Map<String, String> m0 = new HashMap<>();
              m0.put("x", "123");
              m0.put("y", "456");
              m0.put("z", "789");
      
              Map<String, String> m1 = new HashMap<>();
              m1.put("x", "000");
              m1.put("y", "111");
              m1.put("z", "222");
      
              List<Map<String, String>> l = new ArrayList<>(Arrays.asList(m0, m1));
      
              List<String> desiredKeys = Lists.newArrayList("x", "z");
      
              List<Map<String, String>> transformed = l.stream().map(map -> map.entrySet().stream()
                      .filter(e -> desiredKeys.stream().anyMatch(k -> k.equals(e.getKey())))
                      .collect(Collectors.toMap(e -> e.getKey(), p -> p.getValue()))).filter(m -> !m.isEmpty()).collect(Collectors.toList());
      
              System.err.println(l);
              System.err.println(transformed);
          }
      }
      

      【讨论】:

        【解决方案4】:

        试试这个,它应该可以工作:

        Map<String, String> m0 = new HashMap<>();
                m0.put("x", "123");
                m0.put("y", "456");
                m0.put("z", "789");
        
                Map<String, String> m1 = new HashMap<>();
                m1.put("x", "000");
                m1.put("y", "111");
                m0.put("z", "222");
        
                List<Map> l = new ArrayList<>(Arrays.asList(m0, m1));
                List<Map> transformed = new ArrayList<Map>() ;
                l.stream().map(map -> {
                    Set<String> keys = map.keySet() ;
                    Map<String, String> newMap = new HashMap<>();
                    for(String key : keys){
                       if(key.equals("x")|| key.equals("z")) 
                            newMap.put(key, map.get(key).toString()) ;
                    }
                    return newMap ;
                }).forEach(map -> transformed.add(map)); 
        
                System.out.println(transformed);
        

        【讨论】:

          【解决方案5】:

          怎么样:

           tx = StreamEx.of(l)
                        .map(m -> EntryStream.of(m).filterKeys(k -> k.equals("x") || k.equals("z")).toMap())
                        .toList();
          

          StreamEx

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-03-10
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多