これらのインターフェースは両方とも1つのメソッドのみを定義します
public operator fun iterator(): Iterator<T>
ドキュメントによるSequence
と、怠惰になることを意図しています。しかし、Iterable
怠惰ではありませんCollection
か(に裏打ちされていない限り)?
回答:
主な違いは、とのstdlib拡張関数のセマンティクスと実装にIterable<T>
ありSequence<T>
ます。
の場合Sequence<T>
、拡張関数は、Java Streamsの中間操作と同様に、可能な場合は遅延して実行されます。たとえば、Sequence<T>.map { ... }
別のものSequence<R>
を返し、またはのような端末操作が呼び出されるまで、実際にはアイテムを処理しません。toList
fold
このコードを検討してください:
val seq = sequenceOf(1, 2)
val seqMapped: Sequence<Int> = seq.map { print("$it "); it * it } // intermediate
print("before sum ")
val sum = seqMapped.sum() // terminal
印刷します:
before sum 1 2
Sequence<T>
Java Streamsと同様に、ターミナル操作で行われる作業を可能な限り減らしたい場合の怠惰な使用法と効率的なパイプライン化を目的としています。ただし、怠惰はオーバーヘッドをもたらします。これは、小さなコレクションの一般的な単純な変換には望ましくなく、パフォーマンスが低下します。
一般に、いつ必要かを判断する良い方法はありません。そのため、Kotlinではstdlibの怠惰が明示的になり、デフォルトSequence<T>
ですべてので使用されないようにインターフェイスに抽出さIterable
れます。
以下のためにIterable<T>
逆に、と拡張機能、中間演算のセマンティクスは、熱心に働く、すぐにアイテムを処理し、別のものを返しますIterable
。たとえば、マッピング結果をIterable<T>.map { ... }
含むList<R>
を返します。
Iterableの同等のコード:
val lst = listOf(1, 2)
val lstMapped: List<Int> = lst.map { print("$it "); it * it }
print("before sum ")
val sum = lstMapped.sum()
これは印刷されます:
1 2 before sum
上記のように、Iterable<T>
デフォルトでは遅延がなく、このソリューションはそれ自体がうまく機能します。ほとんどの場合、参照の局所性が良好であるため、CPUキャッシュ、予測、プリフェッチなどを利用して、コレクションの複数のコピーでも正常に機能します。十分であり、コレクションが少ない単純なケースでパフォーマンスが向上します。
評価パイプラインをさらに制御する必要がある場合は、Iterable<T>.asSequence()
関数を使用して遅延シーケンスに明示的に変換します。
map
、filter
およびそれがあるので、「怠惰なこと」のための良好なマーカーではないこと、他の人がソースコレクション型以外から決定するのに十分な情報を有していない、とほとんどのコレクションはまた、反復処理可能であるため、一般的にどこでも。怠惰は安全であるために明示的でなければなりません。
ホットキーの回答を完了する:
シーケンスと反復可能要素が要素全体でどのように反復されるかに注意することが重要です。
シーケンス例:
list.asSequence().filter { field ->
Log.d("Filter", "filter")
field.value > 0
}.map {
Log.d("Map", "Map")
}.forEach {
Log.d("Each", "Each")
}
ログ結果:
フィルタ-マップ-それぞれ; フィルタ-マップ-それぞれ
反復可能な例:
list.filter { field ->
Log.d("Filter", "filter")
field.value > 0
}.map {
Log.d("Map", "Map")
}.forEach {
Log.d("Each", "Each")
}
フィルタ-フィルタ-マップ-マップ-それぞれ-それぞれ
Iterable
のjava.lang.Iterable
インターフェイスに マップされ、JVM
ListやSetなどの一般的に使用されるコレクションによって実装されます。これらのコレクション拡張関数は熱心に評価されます。つまり、入力内のすべての要素を即座に処理し、結果を含む新しいコレクションを返します。これは、コレクション関数を使用して、21歳以上のリストの最初の5人の名前を取得する簡単な例です。
val people: List<Person> = getPeople() val allowedEntrance = people .filter { it.age >= 21 } .map { it.name } .take(5)
ターゲットプラットフォーム:kotlinv。1.3.61でのJVMRunning最初に、リスト内のすべての個人に対して年齢チェックが実行され、結果が新しいリストに入れられます。次に、フィルター演算子の後に残ったすべてのPersonに対して名前へのマッピングが行われ、最終的にさらに別の新しいリストになります(これは現在
List<String>
)です。最後に、前のリストの最初の5つの要素を含むように作成された最後の新しいリストが1つあります。対照的に、Sequenceは、遅延評価された値のコレクションを表すKotlinの新しい概念です。同じコレクション拡張機能を
Sequence
インターフェイスで使用できますが、これらは日付の処理済み状態を表すシーケンスインスタンスをすぐに返しますが、実際には要素を処理しません。処理を開始するにSequence
は、端末オペレーターで終了する必要があります。これらは基本的に、シーケンスが表すデータを具体的な形式で具体化するためのシーケンスへの要求です。例としては、、、、toList
などtoSet
がありsum
ます。これらが呼び出されると、必要な最小数の要素のみが処理され、要求された結果が生成されます。既存のコレクションをシーケンスに変換するのは非常に簡単です
asSequence
。拡張機能を使用する必要があります。上記のように、ターミナル演算子も追加する必要があります。そうしないと、シーケンスは処理を実行しません(繰り返しますが、怠惰です!)。
val people: List<Person> = getPeople() val allowedEntrance = people.asSequence() .filter { it.age >= 21 } .map { it.name } .take(5) .toList()
ターゲットプラットフォーム:kotlinv。1.3.61でのJVMRunningこの場合、シーケンス内のPersonインスタンスはそれぞれ年齢がチェックされ、合格した場合は名前が抽出され、結果リストに追加されます。これは、5人が見つかるまで、元のリストの各人に対して繰り返されます。この時点で、toList関数はリストを返し、の残りのユーザー
Sequence
は処理されません。シーケンスに追加できる機能もあります。無限の数のアイテムを含めることができます。これを考慮すると、オペレーターが自分のやり方で作業することは理にかなっています。無限のシーケンスのオペレーターは、熱心に作業を行った場合、戻ることはできません。
例として、ターミナルオペレーターが必要とする数の2の累乗を生成するシーケンスを次に示します(これがすぐにオーバーフローするという事実を無視します)。
generateSequence(1) { n -> n * 2 } .take(20) .forEach(::println)
Java
(ほとんどGuava
)ファンにとっておそらく大きな驚き