LazyList: A Lazy Version of List
In this lesson, you will be introduced to LazyLists; the lazy version of Lists.
We'll cover the following
Introduction
A LazyList is an immutable sequence type collection very similar to Lists. What makes a LazyList lazy is that it only computes required elements, allowing it to be infinitely long. Other than this difference, LazyLists have the same performance characteristics as Lists, hence, to avoid repetition, we won’t discuss them here.
Creating a LazyList
Similar to the ::
method used for creating Lists, LazyLists are created with the #::
method. The first element is the head while all other elements are collectively known as the tail. We use LazyList.empty
to end a LazyList, which is equivalent to the Nil
used for ending a List.
val myFirstLazyList = 1.5 #:: 2.5 #:: 3.5 #:: LazyList.emptymyFirstLazyList.foreach(println)
Lazy Computation
A LazyList’s lazy aspect might not have been visible in the example above because we are asking the compiler to print every element of the LazyList; hence, every element needs to be computed. But what would happen if we tell the compiler to print the complete LazyList, instead of printing each element?
To get a better understanding of lazy computation, let’s first print a whole List and see how a nonlazy approach would look.
val wholeList = "orange"::"banana"::"apple"::"grape"::Nilprint(wholeList)
The output shows a List of four elements. Now let’s print a LazyList with the exact same elements.
val wholeLazyList = "orange" #:: "banana" #:: "apple" #:: "grape" #:: LazyList.emptyprint(wholeLazyList)
The output shows a LazyList, but in place of the elements, there is a <not computed>
statement. This is because the elements have not yet been computed.
But when would a LazyList actually come in handy? Imagine you want to create a list of elements from 1 to 100,000,000. Let’s see what would happen if you use a simple List.
val list = List.from(1 to 100000000)
When you run the code snippet above, it will display Execution Timed Out!
This is because when a List is declared, it computes every element in that List regardless of if we require them or not.
Now, let’s look at the LazyList implementation.
val lazyList = LazyList.from(1 to 100000000)
Wow! It barely took any time for the above code snippet to execute. This is the power of LazyLists.
Streams
LazyList was introduced in the latest version of Scala, i.e., 2.13.0. In older versions, instead of LazyLists, Streams are used. They are identical to LazyLists in almost every way. The only difference is that while LazyLists are completely lazy, Streams compute the head element even when not required.
With LazyLists, we end our discussion on collections. Let’s wrap up this chapter with a quiz to test what you have learned so far.