1前書き

Java 8では、

Streams

の概念がコレクション階層に導入されました。

これらはプロセスを機能させるためにいくつかの関数型プログラミングの概念を利用して、非常に読みやすい方法でデータのいくつかの非常に強力な処理を可能にします。

Kotlinの慣用句を使用して、どのようにして同じ機能を実現できるかを調べます。普通のJavaでは利用できない機能についても見ていきます。


2 JavaとKotlin

Java 8では、新しいファンシーAPIは

java.util.stream.Stream

インスタンスと対話するときにのみ使用できます。

良いことは、すべての標準コレクション(

java.util.Collection

を実装するものすべて)に、

Stream

インスタンスを生成できる特定のメソッド

stream()

があることです。


Stream



Collection.

ではないことを覚えておくことは重要です。** これは

java.util.Collection

を実装していないし、Javaの

Collections

の通常のセマンティクスも実装していません。それは

Collection

から派生したものであり、それを処理するために使用され、表示されている各要素に対して操作を実行します。

  • Kotlinでは、最初に変換する必要なしに、すべてのコレクション型が既にこれらの操作をサポートしています。コレクションのセマンティクスが間違っている場合にのみ変換が必要になります。たとえば、

    Set

    には一意の要素がありますが、順序は異なります。

この利点の1つは、

collect()呼び出しを使用して、

Collection

から

Streamへの初期変換、および

Stream

からコレクションへの最終変換が不要なことです。

たとえば、Java 8では、次のように書く必要があります。

someList
  .stream()
  .map()//some operations
  .collect(Collectors.toList());

Kotlinの同等物は非常に簡単です:

someList
  .map()//some operations

  • さらに、Java 8

    Streams

    も再利用できません。**

    Stream

    が消費された後は、再び使用することはできません。

たとえば、次のようには機能しません。

Stream<Integer> someIntegers = integers.stream();
someIntegers.forEach(...);
someIntegers.forEach(...);//an exception

Kotlinでは、これらがすべて通常のコレクションであるという事実は、この問題が発生しないことを意味します。

中間状態は、変数に代入してすばやく共有することができます

。そして期待通りに動作します。


3遅延シーケンス

Java 8

Streams

に関する重要なことの1つは、それらが遅延評価されることです。これは、必要以上の作業が行われないことを意味します。

これは、__Stream内の要素に対して潜在的に高価な操作を実行している場合、または無限シーケンスで作業することを可能にする場合に特に便利です。

たとえば、

IntStream.generate

は、無限の整数の

Stream

を生成します。その上で

findFirst()

を呼び出すと、最初の要素が取得され、無限ループに陥ることはありません。

  • Kotlinでは、コレクションは怠け者** ではなく熱心です。ここでの例外は

    Sequence

    で、遅延評価されます。

次の例に示すように、これは注意すべき重要な違いです。

val result = listOf(1, 2, 3, 4, 5)
  .map { n -> n **  n }
  .filter { n -> n < 10 }
  .first()

これのKotlinバージョンは5つの

map()

操作、5つの

filter()

操作を実行してから最初の値を抽出します。最後の操作の観点からは、これ以上は必要ないため、Java 8バージョンでは

map()



filter()

を1つだけ実行します。

  • Kotlinのすべてのコレクションは、

    asSequence()

    メソッドを使用して遅延シーケンスに変換できます。

上記の例で

List

の代わりに

Sequence

を使用すると、Java 8と同じ数の操作が実行されます。


4 Java 8 __ストリーム操作

Java 8では、

Stream

操作は2つのカテゴリに分類されます。

  • 中間と

  • ターミナル

中間操作は、基本的にある

Stream

を別の遅延に変換します。たとえば、すべての整数の

Stream

をすべての偶数の整数の

Stream

に変換します。

端末オプションは

Stream

メソッドチェーンの最後のステップであり、実際の処理を開始します。

コトリンではそのような区別はありません。代わりに、これらはすべて、コレクションを入力として受け取り、新しい出力を生成する単なる関数です。

Kotlinで熱心なコレクションを使用している場合、これらの操作はすぐに評価されます。Javaと比較すると驚くかもしれません。

遅延する必要がある場合は、最初に

Sequence

に変換することを忘れないでください。


4.1. 中間操作

  • Java 8 Streams APIのほとんどすべての中間操作は、Kotlin ** と同等のものです。ただし、

    Sequence

    クラスの場合を除き、これらは中間操作ではありません。入力コレクションの処理からコレクションが完全に取り込まれるためです。

これらの操作の中で、

filter()



map()



flatMap()



distinct()

、および

sorted()

とまったく同じように機能するものがいくつかあります。 –

limit()



take



skip()

は__drop()になりました。例えば:

val oddSquared = listOf(1, 2, 3, 4, 5)
  .filter { n -> n % 2 == 1 }//1, 3, 5
  .map { n -> n **  n }//1, 9, 25
  .drop(1)//9, 25
  .take(1)//9

これは単一の値「9」 – 32を返します。

  • これらの操作の中には、新しいバージョンを生成するのではなく、提供されたコレクションに出力する追加のバージョン(接尾辞

    “ To”

    ** が付いたもの)もあります。

これは、いくつかの入力コレクションを同じ出力コレクションに処理するのに役立ちます。次に例を示します。

val target = mutableList<Int>()
listOf(1, 2, 3, 4, 5)
  .filterTo(target) { n -> n % 2 == 0 }

これにより、値「2」と「4」がリスト「target」に挿入されます。

  • 直接置換を通常行わない唯一の操作は

    peek()

    ** – フローを中断せずに処理パイプラインの途中で

    Stream

    内のエントリを反復するためにJava 8で使用されます。

熱心なコレクションの代わりにlazy

Sequence

を使用している場合は、

peek

関数を直接置き換える

onEach()

関数があります。ただし、これはこの1つのクラスにしか存在しないため、このクラスが機能するためにはどのタイプを使用しているのかを認識する必要があります。

  • 生活を楽にする標準の中間操作にはいくつかの追加のバリエーションもあります** 。たとえば、

    filter

    操作には、追加バージョン

    filterNotNull()



    filterIsInstance()



    filterNot()

    、および

    filterIndexed()

    があります。

例えば:

listOf(1, 2, 3, 4, 5)
  .map { n -> n **  (n + 1)/2 }
  .mapIndexed { (i, n) -> "Triangular number $i: $n" }

これにより、「Triangular number 3:6」の形式で、最初の5つの三角形の数が生成されます。

もう1つの重要な違いは、

flatMap

操作の動作方法です。 Java 8では、この操作は

Stream

インスタンスを返すために必要ですが、Kotlinでは、どんなコレクション型でも返すことができます。これにより、作業が簡単になります。

例えば:

val letters = listOf("This", "Is", "An", "Example")
  .flatMap { w -> w.toCharArray() }//Produces a List<Char>
  .filter { c -> Character.isUpperCase(c) }

Java 8では、これを機能させるためには2行目を

Arrays.toStream()

でラップする必要があります。


4.2. ターミナル操作

  • Java 8 Streams APIのすべての標準端末操作は、

    collect

    を除いて、Kotlinで直接置き換えられます。

それらのいくつかは異なる名前を持っています:


  • anyMatch()

    – >

    any()


  • allMatch()

    – >

    all()


  • noneMatch()

    – >

    none()

そのうちのいくつかはKotlinがどのように違うかを扱うための追加のバリエーションを持っています –

first()



firstOrNull()

があります。

興味深いケースは

collect

です。 Java 8はこれを使って、提供された戦略を使って

Stream

要素をすべてあるコレクションに集めることができます。

これは任意の

Collector

を提供することを可能にし、それはコレクション内のすべての要素と共に提供され、そしてある種の出力を生成します。これらは

Collectors

ヘルパークラスから使用されますが、必要に応じて独自のものを書くことができます。

  • Kotlinでは、コレクションオブジェクト自体のメンバとして直接利用可能なほとんどすべての標準的なコレクタに直接置き換わるものがあります** – 提供されているコレクタに追加のステップは必要ありません。

ここでの1つの例外は

summarizingDouble

/

summizingInt

/

summizingLong

メソッドです。これらは、平均、カウント、最小、最大、合計をまとめて生成します。これらはそれぞれ個別に製造することができます – それは明らかに高いコストを持っていますが。

別の方法として、for-eachループを使用して管理し、必要に応じて手動で処理することができます。


5 Kotlinでの追加操作

Kotlinはコレクションを追加した操作を追加していますが、Java 8ではこれを実装しなければ不可能です。

上記のように、これらのいくつかは単に標準操作の拡張です。たとえば、新しいコレクションを返すのではなく、既存のコレクションに結果が追加されるように、すべての操作を実行することができます。

多くの場合、問題の要素だけでなく要素のインデックスもラムダに提供することも可能です – 順序付けられたコレクションの場合、インデックスは意味があります。

Kotlinの無効性を明示的に利用する操作もいくつかあります。

List <String?>

に対して

filterNotNull()

を実行して、すべてのnullが削除された

List <String>

を返すことができます。

KotlinではできるがJava 8 Streamsではできない実際の追加操作には、次のものがあります。


  • zip()

    および

    unzip()

    – 2つのコレクションを1つにまとめるために使用されます

ペアのシーケンス。逆にペアのコレクションを
2つのコレクション
**

associate

– コレクションをマップに変換するために使用されます。

コレクション内の各エントリを結果のマップ内のキーと値のペアに変換するためのラムダを提供します。

例えば:

val numbers = listOf(1, 2, 3)
val words = listOf("one", "two", "three")
numbers.zip(words)

これにより、

1から “one”、2から “two”

、および

3から “three”



List <Pair <Int、String >>

が生成されます。

val squares = listOf(1, 2, 3, 4,5)
  .associate { n -> n to n **  n }

これは

Map <Int、Int>

を生成します。キーは1から5までの数字で、値はそれらの値の2乗です。


6. 概要

Java 8から慣れ親しんだストリーム操作のほとんどは、標準のCollectionクラスのKotlinで直接使用でき、最初は

Stream

に変換する必要はありません。

さらに、Kotlinは、使用可能な操作を追加したり、既存の操作にバリエーションを追加したりすることで、これがどのように機能するかをより柔軟にします。

しかし、Kotlinはデフォルトで熱心で、怠け者ではありません。使用されているコレクションの種類に注意を払わないと、追加の作業が行われる可能性があります。