2
  • How will Kotlin work the following code?
  • Will a collection of 5000000 integers be created as temporary collection or will the filter feed its result immediately to the forEach which means, that only 20 integers will be looked at?
  • If not, how would I be able to avoid the intermediate collection?

Code:

class Tests {
    @Test
    fun test() {
        var counter = 0
        (1..10_000_000).filter { it % 2 == 1 }.forEach {
            counter++
            if (counter > 10)
                return
        }
    }
}
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
aschoerk
  • 3,333
  • 2
  • 15
  • 29
  • 1
    note that you can omit parentheses if lambdas are the only argument to a function like `forEach`, I modified your snippet, hope that's fine – s1m0nw1 Jan 15 '18 at 14:53

2 Answers2

5

Your code sample uses operations on Iterable<T>, which works eagerly: the .filter { ... } call will process the whole range and produce a list storing intermediate results.

To alter that, consider using Sequence<T> (e.g. with .asSequence()) that works lazily, so that the intermediate operations such as .filter { ... } produce another lazy sequence and only do the work when items are queried by terminal operations such as .forEach { ... }:

(1..10000000).asSequence()
    .filter { it % 2 == 1 } 
    .forEach { /* ... */ }

See: Kotlin's Iterable and Sequence look exactly same. Why are two types required?

hotkey
  • 140,743
  • 39
  • 371
  • 326
1

You can actually see the answer to your question pretty quickly by just adding println(it) into filter:

//...
.filter {
   println(it)
   it % 2 == 1
}
//...

You'll see every number printed. That's because your processing works eagerly as explained here.

As already suggested, lazy Sequences to the rescue: (1..10_000_000).asSequence()

Now, the println(it) in filter will only print the numbers 1..21, which is definetly preferable in your example.

s1m0nw1
  • 76,759
  • 17
  • 167
  • 196