Exercise 6 - Lazy

What is Lazy<T>

Lazy<T> is a class that implements lazy evaluation in Java. Lazy evaluation means a value is computed only when it's actually needed, not when it's defined.

Motivation:

  • Improves performance by avoiding unnecessary calculations

  • Handles potentially expensive operations only when required

  • Supports operations on values that don't exist yet


As we have seen in the lecture, Lazy has two ways

  1. Use Lambda as Delayed Data

  2. Memoization

And the Lazy<T> we build today combines these two ways!

Example

// Without lazy evaluation - calculates immediately even if never used
int result = expensiveCalculation(); 

// With lazy evaluation - calculation happens only when get() is called
Lazy<Integer> lazyResult = Lazy.of(() -> expensiveCalculation());
// ...later in code...
int value = lazyResult.get(); // calculation happens here

Main methods explanation

Before delving into deeper details, this is the field and constructor for our Lazy<T>

1

of()

The of() is Lazy<T>'s factory method, it support two ways to create a Lazy.

  1. of(T v): Creates a Lazy with an already-computed value

  2. of(Producer<? extends T> s): Creates a Lazy with a producer for delayed computation

Implementation

Example

2

get()

This method is the core of lazy evaluation. It checks if the value is already computed (present in the Maybe container). If available, it returns it; otherwise, it calls the producer to compute the value, stores it for future requests, and returns it. This ensures the computation happens exactly once, the first time it's needed.

Implementation

Example

3

map(Transformer<? super T, ? extends U> t)

The map method allows transformation of a Lazy value into another type without triggering immediate computation. It creates a new Lazy that, when evaluated, will first get this Lazy's value and then apply the transformation function to it. This maintains laziness throughout the chain of operations.

Implementation

Example

4

flatMap(Transformer<? super T, ? extends Lazy<? extends U>> t)

The flatMap method is similar to map but works with transformations that themselves return Lazy values. It prevents ending up with nested Lazy structures (like Lazy<Lazy>). When the resulting Lazy is evaluated, it gets this Lazy's value (this.get()), applies the transformation to get another Lazy (t.transform()), and then gets the value from that second Lazy (the second .get()).

Implementation

Example

5

filter(BooleanCondition<? super T> c)

The filter method returns a new Lazy that, when evaluated, will test the original value against the provided condition. Unlike filters in collections that remove elements, this filter converts the value into a boolean result indicating whether the condition passed. The evaluation of both the original value and the condition is deferred until get() is called.

Implementation

Example

The meaning of Lazy<Boolean> is that this Lazy wrapper wraps a Boolean value, and only when we call its .get(), we can know whether the value wrapped is true or false.

6

combine(Lazy<? extends S> src, Combiner<? super T, ? super S, ? extends R> c)

The combine method allows two Lazy values to be combined into a new Lazy result using a combining function. When evaluated, it will get both values and then apply the combiner function to produce the result. This is useful for operations that require multiple inputs, like addition, concatenation, or any binary operation, while preserving lazy evaluation for all components.

Implementation

Example

7

equals()

Compares Lazy objects based on their computed values.

Implementation

Example

Refer back to the Keys of being lazy, this is concise and important!

Lazy List

Use the above Lazy<T> to build a list that supports:

  1. Delayed evaluation when needed

  2. Memoization

We have come up with the following LazyList.java

In our LazyList, there is no need to keep track of current index or retrieve/insert by index etc.

List::indexOf(T arg): returns the index of the first element whose value equals the argument, determined by the equals method.

No need to perform linear search manually because our Lazy::equals method checks equality by value. This is elegant!

Last updated