{"id":13123,"date":"2017-08-30T10:42:20","date_gmt":"2017-08-30T08:42:20","guid":{"rendered":"https:\/\/touk.pl\/blog\/?p=13123"},"modified":"2022-08-01T13:17:05","modified_gmt":"2022-08-01T11:17:05","slug":"idiomatic-peeking-with-java-stream-api","status":"publish","type":"post","link":"https:\/\/touk.pl\/blog\/2017\/08\/30\/idiomatic-peeking-with-java-stream-api\/","title":{"rendered":"Idiomatic Peeking with Java Stream API"},"content":{"rendered":"<p>The peek() method from the Java Stream API is often misunderstood. Let&#8217;s try to clarify how it works and when should be used.<\/p>\n<p><a title=\"interface in java.util.stream\" href=\"https:\/\/docs.oracle.com\/javase\/8\/docs\/api\/java\/util\/stream\/Stream.html\">Stream<\/a><\/p>\n<p>&nbsp;<\/p>\n<h2 id=\"introduction\">Introduction<\/h2>\n<p>Let&#8217;s start responsibly by <del>RTFM<\/del> inspecting <em>peek()&#8217;s<\/em> <a href=\"https:\/\/docs.oracle.com\/javase\/8\/docs\/api\/java\/util\/stream\/Stream.html#peek-java.util.function.Consumer-\">user&#8217;s manual.<\/a><\/p>\n<blockquote><p>Returns a stream consisting of the elements of this stream, additionally performing <strong>the provided action<\/strong> on each element as elements are consumed from the resulting stream.<br \/>\nSounds pretty straightforward. We can use it for applying side-effects for every consumed Stream element:<\/p><\/blockquote>\n<pre>Stream.of(\"one\", \"two\", \"three\")\r\n  .peek(e -&gt; System.out.println(e + \" was consumed by peek().\"))\r\n  .collect(Collectors.toList());<\/pre>\n<p>The result of such operation is not surprising:<\/p>\n<pre>one was consumed by peek().\r\ntwo was consumed by peek().\r\nthree was consumed by peek().<\/pre>\n<h4 id=\"stream-peek-lazy-evaluation\">Stream\/peek Lazy Evaluation<\/h4>\n<p>but&#8230; what will happen if we replace the terminal <em>collect()<\/em> operation with a <em>forEach()?<\/em><\/p>\n<pre>Stream.of(\"one\", \"two\", \"three\")\r\n  .peek(e -&gt; System.out.println(e + \" was consumed by peek().\"))\r\n  .forEach(System.out::println);<\/pre>\n<p>It might be tempting to think that we&#8217;ll see a series of peek() logs followed by a series of for-each logs but this is not the case:<\/p>\n<pre>one was consumed by peek().\r\none\r\ntwo was consumed by peek().\r\ntwo\r\nthree was consumed by peek().\r\nthree<\/pre>\n<p>It gets even more interesting if we get rid of the <em>forEach<\/em> call:<\/p>\n<pre>Stream.of(\"one\", \"two\", \"three\")\r\n  .peek(e -&gt; System.out.println(e + \" was consumed by peek().\"));<\/pre>\n<p>the result would be nothing:<\/p>\n<pre><\/pre>\n<p>As <a href=\"http:\/\/docs.oracle.com\/javase\/8\/docs\/api\/java\/util\/stream\/package-summary.html#StreamOps\">JavaDoc<\/a> states:<\/p>\n<blockquote><p>Intermediate operations return a new stream. 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. Traversal of the pipeline source does not begin until the terminal operation of the pipeline is executed.<br \/>\nSo, because of the\u00a0<a href=\"https:\/\/en.wikipedia.org\/wiki\/Lazy_evaluation\">lazy evaluation<\/a> Stream pipelines are always being traversed &#8220;vertically&#8221; and not &#8220;horizontally&#8221; which allows to avoid doing unnecessary calculations. Additionally, the traversal is triggered only when a terminal method is present. Hence, the observed behaviour.<\/p><\/blockquote>\n<p>This is why it&#8217;s possible to represent and manipulate <a href=\"http:\/\/www.baeldung.com\/java-inifinite-streams\">infinite\u00a0sequences<\/a> using Streams. (Because where do you store an infinite sequence? In the cloud?):<\/p>\n<pre>Stream.iterate(0, i -&gt; i + 1)\r\n  .peek(System.out::println)\r\n  .findFirst();<\/pre>\n<p>The above operation completes almost immediately because of the lazy character of Stream traversal. Of course, if you try to collect the whole infinite sequence to some data structure, even laziness will not save you.<\/p>\n<p>So, we can see that <strong>peek() can&#8217;t be treated as an intermediate for-each replacement because it invokes the passed Consumer only on elements that are visited by the Stream.<\/strong><\/p>\n<p>Unfortunately, <a href=\"https:\/\/blog.jooq.org\/2017\/07\/03\/are-java-8-streams-truly-lazy-not-completely\/\">Streams do not always behave entirely lazily.<\/a><\/p>\n<h2 id=\"proper-usage\">Proper Usage<\/h2>\n<p>Further inspection of the <a href=\"https:\/\/docs.oracle.com\/javase\/8\/docs\/api\/java\/util\/stream\/Stream.html#peek-java.util.function.Consumer-\">official docs reveals a note<\/a>:<\/p>\n<blockquote><p>This method exists <strong>mainly<\/strong> to support debugging, where you want to see the elements as they flow past a certain point in a pipeline<br \/>\nThe point of confusion is the &#8220;mainly&#8221; word. Let&#8217;s have a peek(pun intended) at the English dictionary:<\/p><\/blockquote>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-213\" src=\"http:\/\/4comprehension.com\/wp-content\/uploads\/2017\/08\/Screen-Shot-2017-08-24-at-12.45.09.png\" alt=\"\" width=\"589\" height=\"207\" \/><\/p>\n<p>We can see that <strong>non-debugging usages are not forbidden nor discouraged.<\/strong><\/p>\n<p>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. Let&#8217;s use <a href=\"http:\/\/classicprogrammerpaintings.com\/post\/142649999919\/javautildate-salvador-dali-oil-on-canvas-1931\">the infamous mutable java.util.Date:<\/a><\/p>\n<pre>Stream.of(Date.from(Instant.EPOCH))\r\n  .peek(d -&gt; d.setTime(Long.MAX_VALUE))\r\n  .forEach(System.out::println);\r\n\r\n\/\/ Sun Aug 17 08:12:55 CET 292278994<\/pre>\n<p>and we can observe that the result is far away from the standard epoch, which means the mutating operation was indeed applied.<\/p>\n<p><strong>The problem is that this behaviour is highly deceiving because certain Stream implementations can optimize out peek() calls.<\/strong><\/p>\n<p>This gets clarified in the early draft of <a href=\"http:\/\/download.java.net\/java\/jdk9\/docs\/api\/java\/util\/stream\/Stream.html#peek-java.util.function.Consumer-\">JDK 9&#8217;s docs<\/a>\u00a0eventually:<\/p>\n<blockquote><p>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 <code>findFirst<\/code>, or in the example described in <a href=\"http:\/\/download.java.net\/java\/jdk9\/docs\/api\/java\/util\/stream\/Stream.html#count--\"><code>count()<\/code><\/a>), the action will not be invoked for those elements. (&#8230;) An 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.<br \/>\nSo, now it&#8217;s clear that it might not be the best choice if we want to perform some side effects deterministically.<\/p><\/blockquote>\n<p>According to this, peek() might not be even that reliable debugging tool after all.<\/p>\n<h2 id=\"key-takeaways\">Key Takeaways<\/h2>\n<ul>\n<li>The peek() method works fine as a debugging tool when we want to see what is being consumed by a Stream<\/li>\n<li>It seems to work fine when applying mutating operations but <strong>should not be used this way<\/strong> because this behaviour is non-deterministic\u00a0due to the possibility of certain peek() calls being omitted due to internal optimization<\/li>\n<li>The discussion, whether mutation operations should be allowed or not, would never take place if we were restricted to operate only on immutable values<\/li>\n<li>We have around\u00a0292276977 years before we run out of java.util.Date range<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"The peek() method from the Java Stream API is often misunderstood. Let&#8217;s try to clarify how it works&hellip;\n","protected":false},"author":68,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11],"tags":[],"class_list":{"0":"post-13123","1":"post","2":"type-post","3":"status-publish","4":"format-standard","6":"category-development-design"},"_links":{"self":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts\/13123","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/users\/68"}],"replies":[{"embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/comments?post=13123"}],"version-history":[{"count":7,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts\/13123\/revisions"}],"predecessor-version":[{"id":14765,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts\/13123\/revisions\/14765"}],"wp:attachment":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/media?parent=13123"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/categories?post=13123"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/tags?post=13123"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}