KotlinのSequenceとは
Sequenceとは、Kotlinの標準ライブラリにある遅延リストを表す型です。
Listと似たメソッドを持ちますが、その実行自体は遅延されるというやつです。
こういうやつです。
val numberSeq: Sequence<Int> = (1 until 10).asSequence()
val resultSeq: List<Int> = numberSeq
.map { it * 2 }
.filter { it % 3 == 0 }
.toList()
println(resultSeq) //6, 12, 18
参考までにSequenceの代わりにListを使って同じ処理を行った場合のコードも載せます。
ListとSequenceの間で、利用できるメソッド等ほぼ違いがないことがわかるかと思います。
val numberList: List<Int> = (1 until 10).toList()
val resultList: List<Int> = numberList
.map { it * 2 }
.filter { it % 3 == 0 }
println(resultList) //6, 12, 18
このSequenceのListとの内部挙動の違いについて考えてみたいと思います。
違い1: Sequenceは実行がステップごとではなく要素ごと
ListはStep-by-stepに, SequenceはElement-by-elementに実行されるという大きな違いがあります。
意味不明だと思うのでコードを使って説明します。
List: Step-by-stepの挙動
以下のコードだと…
//List example
val numberList = (1..3).toList()
val resultList = numberList
.map { println("1st map: $it"); it + 1 }
.map { println("2nd map: $it"); it + 2 }
println(resultList)
以下のように出力されます。
1st map: 1
1st map: 2
1st map: 3
2nd map: 2
2nd map: 3
2nd map: 4
[4, 5, 6]
この結果を見ると、1つ目のmap関数が全要素(numberListの数値1~3)に対して適用された後、2つ目のmap関数が実行されているのがわかるかと思います。
つまりListの場合、ステップごとの実行になっています。
Sequence: Element-by-elementの挙動
Listの例と同じロジックを以下のようなコードで実装し、実行すると…
//Sequence example
val numberSeq = (1..3).asSequence()
val resultSeq = numberSeq
.map { println("1st map: $it"); it + 1 }
.map { println("2nd map: $it"); it + 2 }
.toList()
println(resultSeq)
以下のように出力されます(ちなみに、ログの最初6行はtoList()
実行時に出力されています)。
1st map: 1
2nd map: 2
1st map: 2
2nd map: 3
1st map: 3
2nd map: 4
[4, 5, 6]
この結果を見ると、まずnumberListの最初の要素(数値1)に対して1つ目・2つ目のmap関数を順次実行し、その後が次の要素の処理へと移っていっているのがわかるかと思います。
つまりSequenceの場合、要素ごとの実行になっています。
違い2: Sequenceは遅延実行
遅延リストなので当然遅延実行となります。
前述の例ですと、Listの以下のコードは.map
を呼び出すたびにprintln
が実行されコンソール出力されます。
//List example
val numberList = (1..3).toList()
val resultList = numberList
.map { println("1st map: $it"); it + 1 }
.map { println("2nd map: $it"); it + 2 }
Sequenceの場合は最後にtoList()
を呼び出した際に.map
内のprintln
が実行されコンソール出力されます。
(=toList()
を呼び出すまでは.map
をいくら呼んでも.map
内の中身は実行されない)
//Sequence example
val numberSeq = (1..3).asSequence()
val resultSeq = numberSeq
.map { println("1st map: $it"); it + 1 }
.map { println("2nd map: $it"); it + 2 }
.toList()
こういった遅延実行を開始するトリガーとなる関数は.toList()
の他にも、.count()
等計算をしなければ結果が生成できないような関数がそれにあたります。