10
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

KotlinのSequenceとListの挙動の違いについて

Posted at

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()等計算をしなければ結果が生成できないような関数がそれにあたります。

参考

Effective Kotlin Item 49

10
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?