The need of using an API comes mostly at the beginning of any data scientist career, while the necessity of creating one from scratch can happen a little bit later: luckily Python helps us in handling both situations in a pretty easy way.
Want more information on peek api? Feel free to contact us.
The formal API (Application Programming Interface) definition is a set of subroutine definitions, protocols, and tools for building software and applications. It refers to a computing'
The peek() method from the Java Stream API is often misunderstood. Let's try to clarify how it works and when should be used.
Stream<T> peek(Consumer<? super T> action)
Let's start responsibly by RTFM inspecting peek()'s user's manual.
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.
Sounds pretty straightforward. We can use it for applying side-effects for every consumed Stream element:
Stream.of("one", "two", "three")
.peek(e -> System.out.println(e + " was consumed by peek()."))
.collect(Collectors.toList());The result of such operation is not surprising:
one was consumed by peek(). two was consumed by peek(). three was consumed by peek().
but' what will happen if we replace the terminal collect() operation with a forEach()?
Stream.of("one", "two", "three")
.peek(e -> System.out.println(e + " was consumed by peek()."))
.forEach(System.out::println);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(). one two was consumed by peek(). two three was consumed by peek(). three
It gets even more interesting if we get rid of the forEach() call:
Stream.of("one", "two", "three")
.peek(e -> System.out.println(e + " was consumed by peek()."));the result would be nothing:
As JavaDoc states:
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.
So, because of the lazy evaluation Stream pipelines are always being traversed 'vertically' and not 'horizontally' which allows avoiding doing unnecessary calculations. Additionally, the traversal is triggered only when a terminal method is present. Hence, the observed behaviour.
This is why it's possible to represent and manipulate infinite sequences using Streams. (Because where do you store an infinite sequence? In the cloud?)
Stream.iterate(0, i -> i + 1) .peek(System.out::println) .findFirst();
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.
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.
The company is the world’s best peek o rings supplier. We are your one-stop shop for all needs. Our staff are highly-specialized and will help you find the product you need.
Unfortunately, Streams do not always behave entirely lazily.
Further inspection of the official docs reveals a note:
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 point of confusion is the 'mainly' word. Let's have a peek(pun intended) into the English dictionary:
We can see that non-debugging usages are neither forbidden nor discouraged.
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's use the infamous mutable java.util.Date:
Stream.of(Date.from(Instant.EPOCH)) .peek(d -> d.setTime(Long.MAX_VALUE)) .forEach(System.out::println); // Sun Aug 17 08:12:55 CET
and we can observe that the result is far away from the standard epoch, which means the mutating operation was indeed applied.
The problem is that this behaviour is highly deceiving because certain Stream implementations can optimize out peek() calls.
This gets clarified in the early draft of JDK 9's docs eventually:
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 incount()), the action will not be invoked for those elements. (')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.
So, now it's clear that it might not be the best choice if we want to perform some side effects deterministically.
And we can observe this indeed if we write something like this and run it under JDK9:
List.of(1, 2, 3) .stream() .peek(System.out::println) .count();
The result is simply:
According to this, the peek() method might not be even that reliable debugging tool after all.
If you want to learn more, please visit our website surface safety valve.