![]() ![]() We have around 292276977 years till we run out of the night I watched the Netflix movie Glass Onion: A Knives Out Mystery with my daughter Sydney, who’s home for the holidays.The discussion, whether mutation operations should be allowed or not, would never take place if we were restricted to operate only on immutable values.It seems to work fine when applying mutating operations but should not be used this way because this behaviour is non-deterministic due to the possibility of certain peek() calls being omitted due to internal optimization.The peek() method works fine as a debugging tool when we want to see what is being consumed by a Stream.So, now it’s clear that it might not be the best choice if we want to perform some side effects deterministically.Īnd we can observe this indeed if we write something like this and run it under JDK9: List.of(1, 2, 3)Īccording to this, the peek() method might not be even that reliable debugging tool after all. (…)Īn implementation may choose to not execute the stream pipeline (either sequentially or in parallel) if it is capable of computing the count directly from the stream source. In cases where the stream implementation is able to optimize away the production of some or all the elements (such as with short-circuiting operations like findFirst, or in the example described in count()), the action will not be invoked for those elements. This gets clarified in the early draft of JDK 9’s docs eventually: The problem is that this behaviour is highly deceiving because certain Stream implementations can optimize out peek() calls. Let’s use the infamous mutable : Stream.of(om(Instant.EPOCH))Īnd we can observe that the result is far away from the standard epoch, which means the mutating operation was indeed applied. So, technically, we should be able to, e.g., modify the Stream elements on the fly which would not be possible in the immutable, functional world. We can see that non-debugging usages are neither forbidden nor discouraged. Let’s have a peek(pun intended) into the English dictionary: The point of confusion is the “mainly” word. This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline Proper Usageįurther inspection of the official docs reveals a note: Unfortunately, Streams do not always behave entirely lazily. So, we can see that peek() can’t be treated as an intermediate for-each replacement because it invokes the passed Consumer only on elements that are visited by the Stream. Of course, if you try to collect the whole infinite sequence to some data structure, even laziness will not save you. The above operation completes almost immediately because of the lazy character of Stream traversal. (Because where do you store an infinite sequence? In the cloud?) erate(0, i -> i + 1) This is why it’s possible to represent and manipulate infinite sequences using Streams. Additionally, the traversal is triggered only when a terminal method is present. So, because of the lazy evaluation Stream pipelines are always being traversed “vertically” and not “horizontally” which allows avoiding doing unnecessary calculations. Traversal of the pipeline source does not begin until the terminal operation of the pipeline is executed. They are always lazy executing an intermediate operation such as filter() does not actually perform any filtering, but instead creates a new stream that, when traversed, contains the elements of the initial stream that match the given predicate. Intermediate operations return a new stream. peek(e -> (e + " was consumed by peek().")) It gets even more interesting if we get rid of the forEach() call: Stream.of("one", "two", "three") It might be tempting to think that we’ll see a series of peek() logs followed by a series of for-each logs but this is not the case: one was consumed by peek(). Stream/peek Lazy Evaluationīut… what will happen if we replace the terminal collect() operation with a forEach()? Stream.of("one", "two", "three") ![]() The result of such operation is not surprising: one was consumed by peek(). We can use it for applying side-effects for every consumed Stream element: Stream.of("one", "two", "three") 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. Let’s start responsibly by RTFM inspecting peek()’s user’s manual. Let’s try to clarify how it works and when should be used. The peek() method from the Java Stream API is often misunderstood.
0 Comments
Leave a Reply. |
AuthorWrite something about yourself. No need to be fancy, just an overview. ArchivesCategories |