使用Java 8流处理地图列表

9

如何将这段代码简化为单个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}]
5个回答

15
当然,你可以将整个操作转换为一个流操作。
// 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,与线性扫描相比,如果映射大于示例代码的三个映射,这也将更有效。


7
您可以尝试像这样做:

您可以尝试类似以下的操作:

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<String, String> 中的所有映射。

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

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

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

1
在这种风格中,更小的版本是使用类似于 map.keySet().retainAll(Arrays.asList("x", "z")) 的东西。 (编辑:但顺便说一句,OP的代码使用收集器进行复制,因此这些并不完全相同。) - Radiodef
即使是基于removeIf的解决方案也会受益于表达式形式,并在首次使用.keySet()时而不是在Entry上调用.getKey()两次。代码如下:l.forEach(map -> map.keySet().removeIf(k -> !k.equals("x") && !k.equals("z"))); - Holger

2

尝试以下代码(我声明了一个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);
    }
}

2

试一下这个,应该可以工作:

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

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

通过StreamEx


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接