Java8 Stream - 使用过滤器以单行收集foreach

时间:2016-06-08 19:56:16

标签: java-8 java-stream

是否可以在单个语句中使用filter(),collect()和foreach()而不是多个语句?

我有一张地图,需要根据某些条件进行过滤,并为内容设置一些值并返回地图。我的当前看起来如下,但我希望所有3个单一陈述。

映射inputMap(包含所有信息)

Map<String, Person> returnMap; 
            returnMap = map.entrySet().stream()
            .filter(p ->  p.getValue().getCourse() == 123)
            .collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue()));

            returnMap.entrySet().stream().forEach((entry) -> {
                Person person= entry.getValue();                
                person.setAction("update");
                person.setLastUpdatedTime(new Date());
            });

可以转换为,

  Map<String, Person> returnMap; 
                returnMap = map.entrySet().stream()
                .filter(p ->  p.getValue().getCourse() == 123)
                .collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue())) 
 .forEach((entry) -> {
                    Person person= entry.getValue();                
                    person.setAction("update");
                    person.setLastUpdatedTime(new Date());
                });

(此代码不起作用)

3 个答案:

答案 0 :(得分:14)

问题是forEach没有返回一个对象,所以你必须处理不同的对象。你可以这样做:

Map<String, Person> returnMap = new HashMap<>(); 
map.entrySet().stream()
              .filter(p ->  p.getValue().getCourse() == 123)
              .forEach((entry) -> {
                    Person person = entry.getValue();                
                    person.setAction("update");
                    person.setLastUpdatedTime(new Date());
                    returnMap.put(entry.getKey(), person);
                });

答案 1 :(得分:2)

坚持用一次手术做这件事没有任何意义。无论你如何写下来,这些都是两个操作。

但是你要考虑的一件事是,除entrySet().stream()之外还有更多的方法来处理所有元素:

Map<String, Person> returnMap = map.entrySet().stream()
    .filter(p ->  p.getValue().getCourse() == 123)
    .collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue()));

returnMap.values().forEach(person -> {          
    person.setAction("update");
    person.setLastUpdatedTime(new Date());
});

如果你仍然坚持让它看起来像一个单一的操作,你可以这样做:

Map<String, Person> returnMap = map.entrySet().stream()
    .filter(p ->  p.getValue().getCourse() == 123)
    .collect(Collectors.collectingAndThen(
        Collectors.toMap(p -> p.getKey(), p -> p.getValue()),
        tmp -> {
            tmp.values().forEach(person -> {          
                person.setAction("update");
                person.setLastUpdatedTime(new Date());
            });
            return tmp;
        })
    );

这在语法上是单个语句,但它与前一个变体完全相同。

答案 2 :(得分:0)

我不明白是否需要按照建议将其分为两个步骤。似乎转换可以在终端操作之前完成,如下所示:

returnMap = map.entrySet().stream()
                .filter(p -> p.getValue().getCourse() == 123)
                .map(e -> {
                    Person p = e.getValue();
                    p.setAction("update");
                    p.setLastUpdatedTime(new Date());
                    return e;
                })
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

如果您对正在流式传输的Map.Entry产生副作用,那么您可以返回一个新的AbstractMap.SimpleEntry,其中包含更新的Person作为其值,但到目前为止还没有一个解决方案没有副作用,因为原始地图中的值正在被修改。无论如何,这是变化:

returnMap = map.entrySet().stream()
                .filter(p -> p.getValue().getCourse() == 123)
                .map(e -> {
                    Person p = e.getValue();
                    p.setAction("update");
                    p.setLastUpdatedTime(new Date());
                    return new AbstractMap.SimpleEntry<>(e.getKey(), p);
                })
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));