Java 8 apply function to all elements of Stream without breaking stream chain

JavaJava 8Java StreamChaining

Java Problem Overview


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.

Java Solutions


Solution 1 - Java

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.

Solution 2 - Java

You are looking for the Stream's map() function.

example:

List<String> strings = stream
.map(Object::toString)
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);

Solution 3 - Java

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

Solution 4 - Java

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.

Solution 5 - Java

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")))

Solution 6 - Java

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.

Solution 7 - Java

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionalexgbelovView Question on Stackoverflow
Solution 1 - JavaBohemianView Answer on Stackoverflow
Solution 2 - JavacsengaView Answer on Stackoverflow
Solution 3 - JavaMr.QView Answer on Stackoverflow
Solution 4 - JavaEugeneView Answer on Stackoverflow
Solution 5 - JavaSaljackView Answer on Stackoverflow
Solution 6 - JavafpsView Answer on Stackoverflow
Solution 7 - JavaRoger HayesView Answer on Stackoverflow