interface Iterable<out T> {
operator fun iterator(): Iterator<T>
}
interface Sequence<out T> {
operator fun iterator(): Iterator<t>
}
val seq = sequenceOf(1, 2, 3)
val filtered = seq.filter { print("f$it"); it % 2 == 1 }
println(filtered) // kotlin.sequences.FilteringSequence@464bee09
val asList = filtered.toList()
println(asList) // f1f2f3[1, 3]
val lists = listOf(1, 2, 3)
val listFiltered = lists.filter { print("f$it"); it % 2 == 1 }
println(listFiltered) // f1f2f3[1, 3]
val seq = sequenceOf(1, 2, 3)
val filtered = seq.filter { print("f$it"); it % 2 == 1 }
println(filtered) // kotlin.sequences.FilteringSequence@464bee09
val asList = filtered.toList() // f1f2f3
// kotlin.sequences.FilteringSequence@464bee09
// f1f2f3
시퀀스의 지연 처리는 다음과 같은 장점을 가진다.
컬렉션에 어떤 처리를 적용하고, 앞의 요소 N개만 필요한 상황은 굉장히 자주 접할 수 있는 상황이다.
시퀀스는 중간 연산이라는 개념을 갖고 있으므로, 앞의 요소 N개에만 원하는 처리를 적용할 수 있다.
// F1, M1, F2, F3, M3,
(1..10).asSequence()
.filter { print("F$it, "); it % 2 == 1 }
.map { print("M$it, "); it * 2 }
.find { it > 5 }
println()
// F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, M1, M3, M5, M7, M9,
(1..10)
.filter { print("F$it, "); it % 2 == 1 }
.map { print("M$it, "); it * 2 }
.find { it > 5 }
중간 처리 단계를 모든 요소에 적용할 필요가 없는 경우 시퀀스를 사용하는 것이 좋다.
시퀀스는 실제 최종 연산이 일어나기 전까지는 컬렉션에 어떠한 처리도 하지 않는다. 따라서 무한 시퀀스(infinite sequence)를 만들고, 필요한 부분까지만 값을 추출하는 것도 가능하다.