Introduction
Sequences in Kotlin are similar to that of the streams in Java, where in the evaluation happens lazily and the volume of data processed in each step in the chain are reducing based on the met criteria.
Sample
Let us consider the below given sample data class
data class Car(val model: String, val year: Int)
We can create a collection of cars so that we can evaluate both and understand the difference between collections and sequence in Kotlin
var cars = listOf(
Car("Toyota", 2021),
Car(model = "Tesla-S", 2022),
Car("Mercedes - Maybach", 2022)
)
Sequence
In the below given snippet of code, we are performing the filtering of cars by name and then printing the year
println(cars.asSequence().filter { it.model == "Toyota" }
.map { it.year }
This produces the output as given below
kotlin.sequences.TransformingSequence@b97c004a
This is because the sequence is lazy and is not evaluated as there is no terminal operator to perform the evaulation.
This can be completed by adding a .toList() at the end of the above snippet as given below.
println(cars.asSequence().filter { it.model == "Toyota" }
.map { it.year }.toList()
In this case, the data is filtered and year value is selected in the map the same is printed.
The same operation with list can be as given below
println(cars.filter { it.model == "Toyota" }.map { it.year }
Though both the sequence and the list provides us with the same response, we have a performance penalty when using a very large collection (in terms of thousands of objects)
To visualize the above statement, we can add println in the steps and run the code to view the difference as given below
Sequences
println(cars.asSequence().filter { println("sequence filtering $it"); it.model == "Toyota" }
.map { println(it);it.year }
.toList())
The output produced by the above line of code is as given below
sequence filtering Car(model=Toyota, year=2021)
Car(model=Toyota, year=2021)
sequence filtering Car(model=Tesla-S, year=2022)
sequence filtering Car(model=Mercedes - Maybach, year=2022)
[2021]
In this case, we find that the filtering has processed all the records and only the Toyota made it up to the map section where we are printing the car alone.
Collections
println(cars.filter { println("list filtering $it"); it.model == "Toyota" }.map { println(it); it.year })
The output of the above snippet is given below,
list filtering Car(model=Toyota, year=2021)
list filtering Car(model=Tesla-S, year=2022)
list filtering Car(model=Mercedes - Maybach, year=2022)
Car(model=Toyota, year=2021)
[2021]
The above indicates that the filtering is done for the entire list and then the matched ones are taken to the next step. This actually involves very poor performance when the list is huge because all the data is processed through the filter and then the subset matched is passed to the map.
However, in sequence it works like an immediate termination on the 1st match. Both have their own use-cases. Its better to choose the right ones based in the requirement. For a known operations that can spawn n / n^2 complexity we can use the list and for the ones that need a 1st match and then process the remaining in a reduced complexity like log(n), we might have to go with the streams.
Comments
Post a Comment