1. ホーム
  2. kotlin

[解決済み] KotlinのIterableとSequenceは全く同じに見える。なぜ2つの型が必要なのでしょうか?

2023-01-23 04:20:03

疑問点

これらのインターフェースは、どちらも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() 関数があります。