使用 Java 8 流处理地图列表

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/43268689/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 07:15:54  来源:igfitidea点击:

Processing a list of maps using Java 8 streams

javalambdajava-stream

提问by user3892260

How can I simplify this code into a single lambda expression? 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".

如何将此代码简化为单个 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);

Output:

输出:

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

回答by Holger

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 Mapand 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. So here, it is much simpler and more efficient (for larger maps) to use

但请注意,流过 aMap和过滤是一种具有线性时间复杂度的操作,因为它会根据过滤器检查每个映射的每个键,而您只有极少数要保留的实际键。所以在这里,使用起来更简单、更有效(对于更大的地图)

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. 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.

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

回答by Jacob G.

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 xor z.

Map<String, String>如果输入键不是xor ,它会简单地删除 each 中的所有映射z

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

回答by shizhz

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

回答by DJAM Silvère Gatien

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

回答by user_3380739

How about:

怎么样:

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

By StreamEx

StreamEx 提供