Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Better way than Stream.peek()

"peek" is to be primarily used for debugging. What if I want to call a method on a stream in the middle of a stream, something that changes the state of the streamed object.

Stream.of("Karl", "Jill", "Jack").map(Test::new).peek(t->t.setLastName("Doe"));

I could do:

Stream.of("Karl", "Jill", "Jack").map(Test::new).map(t->{t.setLastName("Doe"); return t;});

But that seems ugly. Is this something that shouldn't be done or is there a better way to do this?

EDIT: forEach works except that it's a terminal operation, and so you can't keep working on the stream afterwards. I would then expect to make a Collection, do forEach, then start streaming the Collection again.

EDIT: map(Class::processingMethod) is what I'm doing now, but since processingMethod simply returns this, it seems to be a misuse of map. Plus, it doesn't really read like business logic.

FINAL EDIT: I accepted @Holger's answer. Stream.peek cannot be expected to process all the elements on a Stream because it is not a terminal operation. The same goes for map. Even though you might have terminated your stream with something that guarantees it will process all operations, you should not be writing code that expects every user to do so. So, to do processing you should use forEach on a Collection, then start streaming the Collection again if you want to.

like image 545
K.Nicholas Avatar asked Feb 08 '17 15:02

K.Nicholas


People also ask

Which is better stream or for loop?

If you have a small list, loops perform better. If you have a huge list, a parallel stream will perform better. Purely thinking in terms of performance, you shouldn't use a for-each loop with an ArrayList, as it creates an extra Iterator instance that you don't need (for LinkedList it's a different matter).

Which is faster for loop or stream?

Again, the for- loop is faster that the sequential stream operation, but the difference on an ArrayList is not nearly as significant as it was on an array.

What is the difference between MAP and peek in Java 8?

In java-8 the list is populated, but in jdk-9 peek is not called at all. Since you are not using filter or flatmap you are not modifying the size of the Stream and count only needs it's size; thus peek is not called at all. Thus relying on peek is a very bad strategy.

Why would you choose to use a peek operation instead of a foreach operation on a stream?

peek can get the smallest item directly compared with stream. foreach().


1 Answers

You are overusing method references. The simplicity of Test::new is not worth anything, if it complicates the rest of your stream usage.

A clear solution would be:

Stream.of("Karl", "Jill", "Jack")
      .map(first -> { Test t = new Test(first); t.setLastName("Doe"); return t; })
      …

or much better

Stream.of("Karl", "Jill", "Jack").map(first -> new Test(first, "Doe")) …

assuming that the class has the not-so-far-fetched constructor accepting both names.

The code above addresses the use case, where the action manipulates a locally constructed object, so the action is only relevant if the object will be consumed by the subsequent Stream operations. For other cases, where the action has a side effect on objects outside the Stream, abusing map has almost all of the drawbacks of peek explained in “In Java streams, is peek really only for debugging?”

like image 144
Holger Avatar answered Oct 15 '22 06:10

Holger