So, you still struggling trying to understand expressions like this in Java 8+:
Stream.iterate(new long[]{ 0L, 1L }, p->new long[]{ p[1], p[0]+p[1] }).map(p -> p[0]).limit(10).map(p->p+" ").forEach(System.out::print);
Do you have a clue about what this line is about to do, when you hit Run? Yes, you're right, you'll have the Fibonacci sequence of 10 elements printed in your console, like this:
0 1 1 2 3 5 8 13 21 34
It's a tricky one, and if you didn't answer that correctly, I'll try something easier before digging into an explanation.
So, your second chance is right here:
Arrays.asList(1, 2, 2, 3, 4, 4, 4, 5, 6, 8).stream()
.distinct()
.filter(x -> x % 2 == 0)
.map(x -> x * 10)
.skip(1)
.limit(2)
.peek(System.out::println)
.reduce(0, Integer::sum)
Don't need no math right now, because what I wanted to show you is how this works step by step.
So, first of all, what a STREAM is?
Must have in mind is that:
- Streams are not a data structure
- Streams doesn't store or modify data
- It can be defined as a wrapper around a data source
- Supports functional-style operations
- Fast and convenient bulk processing over a data source
- Iteration over elements are sequential and declarative
- Parallel-friendly, immutable and thread-safe
- Lazy evaluation
- Single use
- Don't confuse with Java I/O (ex. FileInputStream)
Besides that, we have:
- Intermediate operations: stream chained operations that return new streams (pipeline)
- Terminal operations: execute the stream and produces a non-stream object (result)
You can create a stream by invoking the methods stream()
or parallelStream()
, or even use Stream.of
, Stream.ofNullable
or Stream.iterate
(like I did for Fibonacci).
Let's try to get back a little bit and "debug" the easy example up there:
Arrays.asList(1, 2, 2, 3, 4, 4, 4, 5, 6, 8)
Creates a new Collection, so we can iterate through its elements via stream
.stream()
Starts a new stream
.distinct()
Retain distinct elements, returning: [1, 2, 3, 4, 5, 6, 8]
.filter(x -> x % 2 == 0)
Filter pair elements by applying mod 2, returning: [2, 4, 6, 8]
.map(x -> x * 10)
Multiplicates each filtered element by 10, returning: [20, 40, 60, 80]
.skip(1)
Skips the first element of the sequence, returning: [40, 60, 80]
.limit(2)
Returns the first 2 elements of the sequence: [40, 60]
This is a "short-circuit" command, i.e., it'll interrupt the stream processing when the condition is match - in this case, when we reach the second element
.peek(System.out::println)
Some kind of "debug" mode of stream, in this case it'll println
each remaining element of the stream operation: 40
and 60
.reduce(0, Integer::sum)
In this case, we're gonna effectively consume the stream and get a Integer result from it: the sum of all elements, where 0
is the starting value, and Integer::sum
invokes the Integer.sum(int a, int b)
function from the Integer class, that will be performed over each remaining element from the stream operation, that is, 40 and 60, then resulting 100.
Now, what about the Fibonacci stuff up there? Right on, let's get back to it.
Stream.iterate
By definition, we have:
Returns an infinite sequential ordered Stream produced by iterative application of a function f to an initial element seed, producing a Stream consisting of seed, f(seed), f(f(seed)), etc.
That said, all we have to do is to provide a seed (i.e., the starting point, or, the initial element) and then a function to be applied to the previous element to produce a new element.
new long[]{ 0L, 1L } // as the 1st parameter
For the Fibonacci "kickstart" we need two elements: 0
and 1
. Then, the sum of both will provide the next element and so on. That said, this array will represent a pair of the first values of the Fibonacci sequence, as the first element of the stream.
p->new long[]{ p[1], p[0]+p[1] }) // as the 2nd parameter
So, this object p
will create a new array containing two elements:
-
p[1]
: which is the previous array in the position 1; -
p[0] + p[1]
: which is the previous array in the position 0 plus its position 1;
.map(p -> p[0])
The thing is, we have a stream of pair elements going on until now, however, our main goal is to print elements of this sequence. To get the "current" element of this operation, by "mapping" the current pair of values of this stream to a single element, in this case, the array in position 0
.
.limit(10)
This is important, as said before, this will Return an infinite sequential ordered Stream by default. So, in order not to have this running forever, with your PC burning out, we define a "limit" to this operation, by processing the first 10 elements only.
.map(p->p+" ")
This is silly, as we want to just print the sequence, it would be nice if we have a separator for each element, so here it is.
.forEach(System.out::print)
The forEach
is a terminal operation as well, and what it'll do is to iterate over the resulting collection of the Stream. So, here we're just printing the current element by using the well-known System.out.print
.
If you're not familiar with expressions like this System.out::print
yet, better check out about method reference, which is a special type of lambda expression, very useful to reduce boilerplate and improve readability. Next chapter suggested to write about is on Java functional programming.
Top comments (0)