Is there a way in Java to apply a function to all the elements of a Stream
without breaking the Stream
chain? I know I can call forEach
, but that method returns a void
, not a Stream
.
There are (at least) 3 ways. For the sake of example code, I've assumed you want to call 2 consumer methods methodA
and methodB
:
A. Use peek()
:
list.stream().peek(x -> methodA(x)).forEach(x -> methodB(x));
Although the docs say only use it for "debug", it works (and it's in production right now)
B. Use map()
to call methodA, then return the element back to the stream:
list.stream().map(x -> {method1(x); return x;}).forEach(x -> methodB(x));
This is probably the most "acceptable" approach.
C. Do two things in the forEach()
:
list.stream().forEach(x -> {method1(x); methodB(x);});
This is the least flexible and may not suit your need.
map
’s function and using peek
for such a (non-debug) side effect, are basically the same. –
Prosthetics method1(x);
would be entirely useless, if it hadn’t side effects. –
Prosthetics peek()
version) when I wanted to collect the elements (to a Set, not timing critical, ie no problem) partway through a stream but also send the tail of the stream to a consumer via forEach(). Not useless. –
Winterfeed Set
was not a side effect? –
Prosthetics forEach
, does not return a stream so you cannot continue to work on the stream. –
Dys forEach
) to do make anything happen, and this question is about calling mutliple consumers in the stream. Option C is one way to do this. –
Winterfeed Stream.forEach
specifically accomplishes the first half of the OP's question, but not the second since you cannot continue the "stream chain" after calling forEach
, e.g. things.filter(...).forEach(o -> o.setActive(true)).reduce(...).map(...).collect(...)
-- forEach
returns void
, which "ends the stream". I was hoping for an apply(...)
function on Stream
, which would be similar to forEach
but apply things in sequence instead of all at once (as the stream is iterated), and allow continuation. –
Dys peek()
twice if you need the stream after the two operations: list.stream().peek(x -> methodA(x)).peek(x -> methodB(x))...
or once with both operations: list.stream().peek(x -> {methodA(x); methodB(x);})...
–
Winterfeed peek()
in my applications written for very large organisations has been running flawlessly in production for many years. –
Winterfeed You are looking for the Stream
's map()
function.
example:
List<String> strings = stream
.map(Object::toString)
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
The best option you have is to apply the map to your stream. which returns a stream consisting of the results of applying the given function to the elements of the stream. For example:
IntStream.range(1, 100)
.boxed()
.map(item->item+3)
.map(item->item*2)...
We are applying several modifications to the stream but in some case we don't want to modify the stream. We just want to visit every element and then pass it down the stream without modification (like the peek() method in the streams API). in such cases, we can
StreamItem peekyMethod(StreamItem streamItemX) {
// .... visit the streamItemX
//Then pass it down the stream
return streamItemX;
}
Not entirely sure what you mean by breaking the stream chain
, but any operation on a Stream
that returns a Stream
will not break or consume your Stream. Streams are consumed by terminal operations
and as you noted the forEach
does not return a Stream<T>
and as such ends the stream, by executing all the intermediate
operations before the forEach and the forEach itself.
In the example that you provided in the comments:
myStream.map(obj -> {obj.foo(); return obj;}
You can't really do this with one liner. Of course you could use a method reference, but then your returned Stream
would be of a different type (assuming foo
returns a type):
myStream.map(Obj::foo) // this will turn into Stream<T>, where T is
// the return type of foo, instead of Stream<Obj>
Besides that your map
operation is stateful
, which is strongly discouraged. Your code will compile and might even work as you want it to - but it might later fail. map
operations should be stateless
.
You can use map
method but you have to create helper method which returns this
. For example:
public class Fluent {
public static <T> Function<T, T> of(Consumer<T> consumer) {
return t -> {
consumer.accept(t);
return t;
};
}
}
And use it when you want to call void
method:
list.stream().map(Fluent.of(SomeClass::method));
or if you want to use it with method with some argument:
list.stream().map(Fluent.of(x -> x.method("hello")))
I think you are looking for Stream.peek
. But read the docs carefully, as it was designed mainly as a debug method. From the docs:
This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline
The action passed to peek
must be non interfering.
I think the cleanest way is to add a mutator to the objects in the stream.
For example,
class Victim {
private String tag;
private Victim withTag(String t)
this.tag = t;
return this;
}
}
List<Victim> base = List.of(new Victim());
Stream<Victim> transformed = base.stream().map(v -> v.withTag("myTag"));
If you prefer (and many will), you can have the withTag method create and return a new Victim; this allows you to make Victim immutable.
© 2022 - 2024 — McMap. All rights reserved.
without breaking the stream chain
? may be an example? – Pocahontasapply
method here:things.stream().apply(o -> o.status = newStatus).map(....)....
Same idea asforEach
, but does not "break the chain", e.g., returns aStream<T>
instead ofvoid
. – Dysstream().map(...)
to do the same thing but it requires you to return the same item back in the lambda, which is silly. – Dys