简体   繁体   English

使用Java 8流处理地图列表

[英]Processing a list of maps using Java 8 streams

How can I simplify this code into a single lambda expression? 如何将此代码简化为单个lambda表达式? The idea is that there is a list of maps and I would like to create a new list of maps, using a filter on the key. 我的想法是有一个地图列表,我想创建一个新的地图列表,使用密钥过滤器。 In this example, I want to remap it so that it only keeps the keys "x" and "z". 在这个例子中,我想重新映射它,使它只保留键“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);

Output: 输出:

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

Of course, you can convert your entire operation into one Stream operation. 当然,您可以将整个操作转换为一个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());

But note that streaming over a Map and filtering is an operation with a linear time complexity, as it will check each key of each map against the filter, while you have only a very small number of actual keys you want to retain. 但请注意,通过Map流式处理和过滤是一种具有线性时间复杂度的操作,因为它会针对过滤器检查每个映射的每个键,而您只需要保留非常少量的实际键。 So here, it is much simpler and more efficient (for larger maps) to use 所以在这里,使用它会更简单,更有效(对于更大的地图)

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());

which will only perform four lookups per map. 每个地图只执行四次查找。 If it bothers you, you could even reduce it to two lookups, however, the constant factor is irrelevant for the overall time complexity, which will be constant time, if the map has a constant time lookup, like HashMap . 如果它困扰你,你甚至可以将它减少到两次查找,但是,常数因素与总体时间复杂度无关,如果地图具有恒定时间查找(如HashMap ,这将是恒定时间。 Even for map's with O(log(n)) lookup time complexity, like TreeMap , this will be more efficient than the linear scan, if the maps are larger than the three mappings of the example code. 即使对于具有O(log(n))查找时间复杂度的地图O(log(n))TreeMap ,如果地图大于示例代码的三个映射,这将比线性扫描更有效。

You can try something like this: 你可以尝试这样的事情:

List<Map<String, String>> l = Arrays.asList(m0, m1);

l.forEach(map -> {
    map.entrySet().removeIf(e -> !e.getKey().equals("x") && !e.getKey().equals("z"));
});

It simply removes all of the mappings in every Map<String, String> if the entry key is not x or z . 如果条目键不是xz Map<String, String>它只是删除每个Map<String, String>中的所有映射。

Edit: You should utilize Radiodef's equivalent, but shorter method! 编辑:您应该使用Radiodef的等效但更短的方法!

List<Map<String, String>> l = Arrays.asList(m0, m1);

l.forEach(map -> map.keySet().retainAll(Arrays.asList("x", "z"));

Try the following code(I declared a list for desiredKeys ): 尝试以下代码(我声明了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);
    }
}

Try this, it should work : 试试这个,它应该工作:

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);

How about: 怎么样:

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

By StreamEx 通过StreamEx

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM