Skip to content

Unveiling the Laziness of Streams. A Journey into Lazy Evaluation

Posted on:August 29, 2023 at 03:13 PM

Hey there, fellow explorers! Welcome back to our journey through the intricate landscapes of functional programming. In our previous excursions, we’ve delved into the enchanting realms of terminal and intermediate operations, acquainting ourselves with their diverse functions. Now, it’s time to unveil a hidden facet of streams that promises to illuminate our understanding further – the laziness of streams.

Table of contents

Open Table of contents

Sections

The Realm of Laziness

Picture this: as our stream flows through a series of operations, it appears that each operation eagerly waits for the previous one to complete before starting. But appearances can be deceiving, for streams operate with a fascinating twist – they embrace laziness.

Streams are Lazy: This concept might sound counterintuitive at first, but it’s a key feature of streams. Intermediate operations on streams are lazier than a cat on a sunny afternoon. What does this mean? It means that intermediate operations don’t actually perform any action until a terminal operation is called upon.

A Practical Glimpse into Laziness

Let’s embark on a practical journey to grasp the notion of stream laziness. Imagine we’re dealing with a list of popular horror books – our mystical source of data. Behold the pipeline:

List<String> popularHorrorBooks = books.stream()
    .filter(book -> book.getGenre().equalsIgnoreCase("Horror"))
    .filter(book -> book.getRating() > 3)
    .collect(Collectors.toList());

1 - Stream Creation: We kick off our journey by creating a stream from our list of books. However, no magic occurs yet. The stream merely waits, biding its time.

2 - Lazy Filtering: With the filter operations, we start filtering our stream – looking for those horror books with ratings above 3. But wait, no books are being summoned from the shadows just yet.

3 - Terminal Operation: Now, it’s time for the grand finale – our terminal operation, collect. This is the moment when the curtain lifts, and the show begins.

A Peek Behind the Curtains

Intrigued by the phenomenon of laziness, we introduce the peek operation – a backstage pass to the stream’s activities. Watch as the elements flow through the pipeline:

List<String> popularHorrorBooks = books.stream()
    .filter(book -> book.getGenre().equalsIgnoreCase("Horror"))
    .peek(book -> System.out.println("Filtered Book: " + book.getName()))
    .filter(book -> book.getRating() > 3)
    .collect(Collectors.toList());

System.out.println("Collection Done");

1 - Stream Creation: Our stream takes its initial form, brimming with potential.

2 - Lazy Filtering with a Peek: As elements pass through the pipeline, we peek behind the scenes to witness the filtered books, one by one. This is akin to glimpsing the actors rehearsing their lines before the grand performance.

3 - Further Lazy Filtering: The show continues as we apply another filter, searching for high-rated horror books. But remember, no books are summoned yet; they’re merely rehearsing their roles.

4 - Terminal Grand Finale: Behold! As we finally invoke the terminal operation, the stream springs into action, collecting the chosen few into our popularHorrorBooks list.

Eager and Lazy: A Balancing Act

Remember this fundamental truth: terminal operations are eager, executing the entire stream pipeline when invoked. On the other hand, intermediate operations are lazy, waiting in the wings until the terminal operation signals them to perform.

And there you have it, fellow voyagers – a glimpse into the laziness that underlies the streams’ intricate dance. As we journey further into the realm of functional programming, may the concept of lazy evaluation add a touch of mystery and excitement to your code explorations.

Until we meet again on this adventurous path, embrace the duality of eagerness and laziness, and let the magic of streams continue to unfold.

You can find the repo for this section of the course Here