In addition to the sequence:
With lists, you make every element perform an action and store them in a new list, whereafter you do another action and again store them in a list.
statefull / stateless
Every operation in a Sequence can be labeled as either statefull or stateless:
- Stateless means that it looks only at one or a couple of elements: eg. map, filter.
- Statefull meanst that it looks at all the elements: eg. foreach, sorted.
If you don’t use statefull operations, a lot of elements will never be evaluated.
You can use peek to see it very well, because it executes an operation on every element that passes by:
sequenceOf(0, 1, 2, 3, 4, 5).map{ it + 1 }.peek{ print(it) }.first() //prints 1
sequenceOf(0, 1, 2, 3, 4, 5).map{ it + 1 }.peek{print(it) }.forEach{} //prints 12345
terminal/intermediate
We can go even further, as you have two kinds of operations:
- terminal: Do something with the elements in the sequence: eg. foreach, first, toList
- intermediate: perform an action with the sequence itself: eg. map, filter, peek
When you never call a terminal operation, none of the elements will do anything:
sequenceOf(0, 1, 2, 3, 4, 5).map{ it + 1 }.peek{ print(it) } //doens't print anything
sequenceOf(0, 1, 2, 3, 4, 5).map{ it + 1 }.peek{print(it) }.sorted() //doesn't print anything
parallel
To run in parallel, you have a couple of options:
-
you can use java’s streams
sequenceOf(0, 1, 2, 3, 4, 5).asStream().parallel().map{it+1}.asSequence()
-
you can create your own implementation (probably worser than the real one, but it was fun to figure out):
fun <S, T> Sequence<S>.map(groupSize: Int, transform: (S) -> T) = buildSequence {
val threads = Executors.newCachedThreadPool()
chunked(groupSize).forEach {
it.map{
threads.submit(Callable { transform(it) })
}.forEach {
yield(it.get() as T)
}
}
}
-
my recommendation: use channels. This makes use of coroutines.