KotlinではSequenceは遅延シーケンスであり、大きなデータセットや無限のデータセットを効率的に扱うことができます。Sequence(例えば、map, filter)への操作は即座には実行されず、代わりにアクションのチェーンが構築され、必要に応じて(終端操作が発生したとき、例えばtoListの時に)計算が行われます。
これが重要な理由:
コードの例:
val numbers = generateSequence(1) { it + 1 } .filter { it % 2 == 0 } .map { it * it } .take(5) .toList() println(numbers) // [4, 16, 36, 64, 100]
この例では、シーケンスの最初の5要素だけが実際に計算されます。このシーケンスは潜在的に無限です。
シーケンスを使用するべき時:
通常のKotlinコレクションにおける
mapおよびfilterタイプの演算子が遅延であることを保証できますか?その理由は?
回答: いいえ、Kotlinの標準コレクション操作(List.map、List.filterなど)は_厳密に貪欲です_。各操作は中間コレクションを生成し、すぐにすべての要素を処理します。遅延はシーケンスを使用してのみサポートされます。
例:
val data = listOf(1,2,3,4) val mapped = data.map { println("Mapping $it"); it * 2 } // すぐにすべての値を表示
逸話
大きなCSVファイル(書き込み時に最大ギガバイト)を処理するプロジェクトでは、コレクションが最初にListに完全に読み込まれ、その後、chain map/filterを適用していました。アプリケーションはOutOfMemoryに陥り、問題はListをSequenceに置き換えて、map/filterが巨大な中間リストを生成しないように解決されました。
逸話
複数のデータベースからレポートを集約するバックエンドサービスは、最初に検索結果をListに蓄積し、その後フィルタリングしていました。リクエストが遅くなり、GCのラグが観察されました。ジェネレーターとSequenceに置き換えることで、データをリアルタイムで集約できるようになり、遅延が大幅に減少しました。
逸話
Javaから移行されたプロジェクトが、データの大きなストリームを扱う際に標準のKotlin拡張(map、filter)を使用し、Sequenceへの移行なしに遅延することを前提にしました。これは重大なメモリリークと本番環境での突然のクラッシュを引き起こしました。