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
Cannot make filter->forEach->collect in one 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 typevoid
.
无法调用
collect(Collectors.toList())
原始类型void
。
Do I need 2 streams for that?
我需要 2 个流吗?
采纳答案by Grzegorz Piwowarek
The forEach
is 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 map
which 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 withState
is 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 forEach
with map
.
替换forEach
为map
。
items.stream()
.filter(s-> s.contains("B"))
.map(s-> {s.setState("ok");return s;})
.collect(Collectors.toList());
forEach
and collect
are bothterminal operations - Streams must have just one. Anything that returns a Stream<T>
is a intermediate operation
, anything other is a terminal operation
.
forEach
而collect
在这两个终端业务-流必须只有一个。返回 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
forEach
is a terminal operation, means that it produces non-stream result. forEach
doesn't produces anything and collect
returns a collection. What you need is a stream operation that modifies elements for your needs. This operation is map
which 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 peek
whose 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
从流返回:新流
回答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);
}