无法在一个流中生成过滤器> forEach->收集?

时间:2017-08-21 09:07:28

标签: java lambda java-8 java-stream

我想实现这样的目标:

items.stream()
    .filter(s-> s.contains("B"))
    .forEach(s-> s.setState("ok"))
.collect(Collectors.toList());

过滤,然后从过滤结果中更改属性,然后将结果收集到列表中。但是,调试器说:

  

无法在基本类型collect(Collectors.toList())上调用void

我需要2个流吗?

7 个答案:

答案 0 :(得分:14)

forEach旨在成为终端操作,是的 - 您在调用后无法执行任何操作。

惯用的方法是首先应用转换,然后collect()将所有内容应用到所需的数据结构。

可以使用专为非变异操作设计的map来执行转换。

如果您正在执行非变异操作:

 items.stream()
   .filter(s -> s.contains("B"))
   .map(s -> s.withState("ok"))
   .collect(Collectors.toList());

其中withState是一个返回原始对象副本的方法,包括提供的更改。

如果您正在执行副作用:

items.stream()
  .filter(s -> s.contains("B"))
  .collect(Collectors.toList());

items.forEach(s -> s.setState("ok"))

答案 1 :(得分:11)

forEach替换为map

 items.stream()
      .filter(s-> s.contains("B"))
      .map(s-> {s.setState("ok");return s;})
      .collect(Collectors.toList());

forEachcollect 都是终端操作 - Streams必须只有一个。任何返回Stream<T>的内容都是intermediate operation,其他任何内容都是terminal operation

答案 2 :(得分:4)

在没有充分理由的情况下,抵制在流内使用副作用的冲动。创建新列表,然后应用更改:

List<MyObj> toProcess = items.stream()
    .filter(s -> s.contains("B"))
    .collect(toList());

toProcess.forEach(s -> s.setState("ok"));

答案 3 :(得分:4)

forEach是一个终端操作,意味着它产生非流结果。 forEach不生成任何内容,collect返回集合。您需要的是一个流操作,可根据您的需要修改元素。此操作为map,允许您指定要应用于输入流的每个元素的函数,并生成转换后的元素流。所以你需要这样的东西:

items.stream()
     .filter (s -> s.contains("B"))
     .map    (s -> { s.setState("ok"); return s; }) // need to return a value here
     .collect(Collectors.toList());

另一种方法是使用peek,其意图是将函数应用于遍历的每个元素(但其主要目的是用于调试):

items.stream()
     .filter (s -> s.contains("B"))
     .peek   (s -> s.setState("ok")) // no need to return a value here
     .collect(Collectors.toList());

答案 4 :(得分:2)

您无法在同一个Stream上执行两个终端操作。

您可以在中间操作中设置对象的状态,例如map

List<YourClass> list = 
    items.stream()
         .filter(s-> s.contains("B"))
         .map(s-> {
                      s.setState("ok"); 
                      return s;
                  })
         .collect(Collectors.toList());

答案 5 :(得分:1)

 items.stream()
      .filter(s-> s.contains("B"))
      .peek(s-> s.setState("ok"))
      .collect(Collectors.toList());
  

Stream peek(消费者操作)返回包含以下内容的流   此流的元素,另外执行提供的   每个元素上的操作,因为元素从结果中消耗   流。这是一个中间操作。

     

对于并行流管道,可以在任何位置调用该动作   时间和元素在任何线程中都可以通过   上游操作。如果操作修改了共享状态,则为   负责提供所需的同步。

     

API注意:此方法主要用于支持调试,您可以在其中   希望看到元素流过某个点中的某个点   管道:

 Stream.of("one", "two", "three", "four")
     .filter(e -> e.length() > 3)
     .peek(e -> System.out.println("Filtered value: " + e))
     .map(String::toUpperCase)
     .peek(e -> System.out.println("Mapped value: " + e))
     .collect(Collectors.toList());   Parameters: action - a non-interfering action to perform on the elements as they are consumed
     

从流中返回:新流

https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#peek-java.util.function.Consumer-

答案 6 :(得分:0)

public static void main(String[] args) {
    // Create and populate the Test List
    List<Object> objectList = new ArrayList<>();
    objectList.add("s");
    objectList.add(1);
    objectList.add(5L);
    objectList.add(7D);
    objectList.add(Boolean.TRUE);

    // Filter by some condition and collect
    List<Object> targetObjectList = 
        objectList.stream().filter(o -> o instanceof String)
        .collect(Collectors.toList());

    // Check
    targetObjectList.forEach(System.out::println);
}