Hanbit the Developer

Kotlin Documentation | Sequences 본문

Mobile/Kotlin

Kotlin Documentation | Sequences

hanbikan 2023. 5. 24. 13:54

Kotlin Documentation 시리즈에 대해

Category: Standard library - Collections

문서 링크: https://kotlinlang.org/docs/sequences.html


Iterable과 유사하지만 lazily하게 작동한다.

Sequence는 처리를 하나씩 하지만, Iterable은 모든 컬렉션을 모두 처리한 뒤 다음 절차를 밟는다.

따라서 Sequence를 사용하면, ‘중간 결과물’ 만드는 과정을 제거하여 성능을 향상시킬 수 있다. 하지만 작은 컬렉션이나 간단한 계산에 대해서는 오히려 오버헤드를 증가시킬 수 있다. 따라서 상황에 맞게 Sequence 또는 Iterable을 사용해야 한다.

⇒ 컬렉션이 작거나 계산이 단순할 경우 Iterable, 아닌 경우 Sequence를 사용한다.

Construct

From elements

val numbersSequence = sequenceOf("four", "three", "two", "one")

From an Iterable

val numbers = listOf("one", "two", "three", "four")
val numbersSequence = numbers.asSequence()

From a function

시작 원소를 generateSequence()의 파라미터로 지정할 수 있으며, null을 리턴하면 생성을 그만두기 때문에, oddNumbers는 원소를 무한대로 갖는다.

val oddNumbers = generateSequence(1) { it + 2 } // `it` is the previous element
println(oddNumbers.take(5).toList())
//println(oddNumbers.count())     // error: the sequence is infinite

From chunks

yieldAll()은 Iterable, Iterator, Sequence를 인자로 받을 수 있다. 특히 Sequence는 무한일 수 있으나, 무한한 Sequence 이후에 오는 것들은 실행되지 않는다.

val oddNumbers = sequence {
    yield(1)
    yieldAll(listOf(3, 5))
    yieldAll(generateSequence(10) { it + 2 })
		yield(123) // Will not be called
}
println(oddNumbers.take(15).toList())

Output: [1, 3, 5, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32]

Sequence operations

  • Stateless operations
    • map(), filter() 등
    • take(), drop() 등: 소량의 state 요구
  • Stateful operations: 다량의 state 요구(일반적으로 시퀸스 원소 개수에 비례)

시퀸스 명령어가 또다른 시퀸스를 반환하는 경우 해당 명령을 intermediate, 그렇지 않은 경우에는 terminal(예시: toList(), sum())이라고 부른다.

Sequence processing example

Iterable

val words = "The quick brown fox jumps over the lazy dog".split(" ")
val lengthsList = words.filter { println("filter: $it"); it.length > 3 }
    .map { println("length: ${it.length}"); it.length }
    .take(4)

println("Lengths of first 4 words longer than 3 chars:")
println(lengthsList)
filter: The
filter: quick
filter: brown
filter: fox
filter: jumps
filter: over
filter: the
filter: lazy
filter: dog
length: 5
length: 5
length: 5
length: 4
length: 4
Lengths of first 4 words longer than 3 chars:
[5, 5, 5, 4]

Sequence

val words = "The quick brown fox jumps over the lazy dog".split(" ")
//convert the List to a Sequence
val wordsSequence = words.asSequence()

val lengthsSequence = wordsSequence.filter { println("filter: $it"); it.length > 3 }
    .map { println("length: ${it.length}"); it.length }
    .take(4)

println("Lengths of first 4 words longer than 3 chars")
// terminal operation: obtaining the result as a List
println(lengthsSequence.toList())
Lengths of first 4 words longer than 3 chars
filter: The
filter: quick
length: 5
filter: brown
length: 5
filter: fox
filter: jumps
length: 5
filter: over
length: 4
[5, 5, 5, 4]

*원소 하나씩 처리되는 것을 확인할 수 있다. 즉, 처리 과정이 다르다. 또한 중간 처리 과정이 Iterable은 23회, Sequence는 18회이다.