Sequenceとは?
Kotlinにおけるコレクション遅延操作の仕組み。
Java8で導入されたStreamに似ており、各要素を直列に評価したあと、最終的な結果を取得する。
こう書くと難しそうなので、実際のコードを見てもらったほうが早いと思います。
Sequenceを使うと何が嬉しいか
ループ回数が少なくなる
Sequenceを使わない場合
(1..3).map { it * 10 } .map { it * 10 } .map { it * 10 }
このような処理の場合、通常のCollectionのまま操作を行うと、map毎にループが発生し、Listを生成します。
実際に行われている処理は下記のような感じで、ループ回数は9回、Listは4つ生成されているのがわかりますね。
val list = mutableListOf(1, 2, 3) val list2 = list.map { it * 10 } val list3 = list2.map { it * 10 } val list4 = list3.map { it * 10 }
Sequenceを使った場合
Sequenceを使った場合は、このように書きます。
(1..3).asSequence() .map { it * 10 } .map { it * 10 } .map { it * 10 } .toList()
見た目はほぼ変わらないのですが、実際に行われる処理は全く異なります。
val list = mutableListOf(1, 2, 3) val list2 = list.map { val result1 = it * 10 val result2 = result1 * 10 result2 * 10 } println(list2)
生成されるListは2つ、ループ回数は3回と、大幅に計算量が減っています。
不要な計算をスキップできる
ループ回数よりもこちらのほうが、計算量に大きな差が出ます。
Sequenceを使わない場合
(1..3).map { it * 10 } .map { it * 10 } .map { it * 10 } .first{ it == 2000 }
このような処理の場合、first()
の手前まですべての要素を処理してから結果を生成します。
実際に行われている処理は下記のような感じで、先述した計算に加えて、
first()
で2000に行き当たるまでの2回計算回数が増えています。
また、Listも一つ多く生成されて5個になっていますね。
val list = mutableListOf(1, 2, 3) val list2 = list.map { it * 10 } val list3 = list2.map { it * 10 } val list4 = list3.map { it * 10 } val list5 = list4.first{ it == 2000 }
Sequenceを使った場合
Sequenceを使った場合は、このように書きます。
(1..3).asSequence() .map { it * 10 } .map { it * 10 } .map { it * 10 } .first{ it == 2000 }
こちらも見た目はほぼ変わらないのですが、実際に行われる処理は全く異なります。
val list = mutableListOf(1, 2, 3) val list2 = list.first { val result1 = it * 10 val result2 = result1 * 10 val result3 = result2 * 10 result3 == 2000 } println(list2)
生成されるListが2つなのは前回と同じですが、この場合なんとループ回数が2回で終わります。
要素をひとつずつ処理するので、 first()
や take()
などのように、
全ての要素を処理する必要がないケースで絶大な効果を発揮するのです。
まとめ
- Kotlinで、Collectionに対して複数の処理を行う場合は
asSequence()
でSequence
として扱うと良いよ - 全ての要素を処理する必要がないケースではほぼ必須レベルだよ