Java 不能在一个流中制作 filter->forEach->collect?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/45793226/
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-08-12 02:10:42  来源:igfitidea点击:

Cannot make filter->forEach->collect in one stream?

javalambdajava-8java-stream

提问by nimo23

I want to achieve something like this:

我想实现这样的目标:

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

filter, then change a property from the filtered result, then collect the result to a list. However, the debugger says:

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

Cannot invoke collect(Collectors.toList())on the primitive type void.

无法调用collect(Collectors.toList())原始类型void

Do I need 2 streams for that?

我需要 2 个流吗?

采纳答案by Grzegorz Piwowarek

The forEachis designed to be a terminal operationand yes - you can't do anything after you call it.

forEach被设计为一个终端操作,是的 - 调用它后你不能做任何事情。

The idiomatic way would be to apply a transformation first and then collect()everything to the desired data structure.

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

The transformation can be performed using mapwhich is designed for non-mutating operations.

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

If you are performing a non-mutating operation:

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

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

where withStateis a method that returns a copy of the original object including the provided change.

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



If you are performing a side effect:

如果您正在执行副作用:

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

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

回答by Eugene

Replace forEachwith map.

替换forEachmap

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

forEachand collectare bothterminal operations - Streams must have just one. Anything that returns a Stream<T>is a intermediate operation, anything other is a terminal operation.

forEachcollect这两个终端业务-流必须只有一个。返回 aStream<T>intermediate operation任何东西都是 a ,其他任何东西都是 a terminal operation

回答by Eran

You cannot execute two terminal operations on the same Stream.

您不能在同一个 Stream 上执行两个终端操作。

You can set the state of the object in an intermediate operation, such as map:

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

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

回答by Misha

Resist the urge to use side-effects from inside the stream without a very good reason. Make the new list and then apply the changes:

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

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

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

回答by Jean-Baptiste Yunès

forEachis a terminal operation, means that it produces non-stream result. forEachdoesn't produces anything and collectreturns a collection. What you need is a stream operation that modifies elements for your needs. This operation is mapwhich lets you specify a function to be applied to each element of the input stream and produces a transformed stream of elements. So you need something like:

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

An alternative is to use peekwhose intention is to apply a function to each element traversing (but its main purpose is for debugging):

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

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

回答by u6856342

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

Stream peek(Consumer action) Returns a stream consisting of the elements of this stream, additionally performing the provided action on each element as elements are consumed from the resulting stream. This is an intermediate operation.

For parallel stream pipelines, the action may be called at whatever time and in whatever thread the element is made available by the upstream operation. If the action modifies shared state, it is responsible for providing the required synchronization.

API Note: This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline:

 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

from the stream Returns: the new stream

Stream peek(Consumer action) 返回一个由该流的元素组成的流,当元素从结果流中被消耗时,还对每个元素执行提供的操作。这是一个中间操作。

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

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-

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

回答by Julian Kolodzey

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