JavaRush /Java Blog /Random-JA /コーヒーブレイク #177。Java 8 の Java Stream の詳細ガイド

コーヒーブレイク #177。Java 8 の Java Stream の詳細ガイド

Random-JA グループに公開済み
出典: Hackernoon この投稿では、コード例と説明とともに Java Stream の操作に関する詳細なチュートリアルを提供します。 コーヒーブレイク #177。 Java 8 の Java Stream の詳細ガイド - 1

Java 8 の Java スレッドの概要

Java 8 の一部として導入された Java Streams は、データのコレクションを操作するために使用されます。これらはそれ自体はデータ構造ではありませんが、順序付けとパイプライン処理によって他のデータ構造から情報を入力し、最終結果を生成するために使用できます。 注: ロシア語では両方の用語が同じ「フロー」と訳されることが多いため、ストリームとスレッドを混同しないことが重要です。ストリームは操作 (ほとんどの場合、データの転送または保存) を実行するためのオブジェクトを表し、スレッド (直訳するとスレッド) は、特定のプログラム コードを他のコード ブランチと並行して実行できるようにするオブジェクトを表します。 Stream は別個のデータ構造ではないため、データ ソースを変更することはありません。Java ストリームには次の機能があります。
  1. Java Streamは「java.util.stream」パッケージを使用して利用できます。次のコードを使用してスクリプトにインポートできます。

    import java.util.stream.* ;

    このコードを使用すると、Java Stream にいくつかの組み込み関数を簡単に実装することもできます。

  2. Java Stream は、Java のコレクションや配列などのデータ コレクションからの入力を受け入れることができます。

  3. Java Stream では、入力データ構造を変更する必要はありません。

  4. Java Stream はソースを変更しません。代わりに、適切なパイプライン メソッドを使用して出力を生成します。

  5. Java ストリームは中間操作と最終操作の対象になります。これについては次のセクションで説明します。

  6. Java Stream では、中間操作はパイプライン化され、遅延評価形式で発生します。それらは端末機能で終わります。これは、Java Stream を使用するための基本的な形式を形成します。

次のセクションでは、必要に応じて Java ストリームを作成するために Java 8 で使用されるさまざまなメソッドを見ていきます。

Java 8 での Java ストリームの作成

Java スレッドはいくつかの方法で作成できます。

1. Stream.empty() メソッドを使用して空のストリームを作成する

空のストリームを作成して、後でコード内で使用できます。Stream.empty()メソッドを使用すると、値を含まない空のストリームが生成されます。この空のストリームは、実行時に null ポインター例外をスキップしたい場合に便利です。これを行うには、次のコマンドを使用できます。
Stream<String> str = Stream.empty();
上記のステートメントは、内部に要素を持たないstr という名前の空のストリームを生成します。これを確認するには、 str.count()という用語を使用してストリームの数またはサイズを確認します。例えば、
System.out.println(str.count());
結果として、出力では0が得られます。

2. Stream.Builder インスタンスを含む Stream.builder() メソッドを使用してストリームを作成します。

Stream Builder を使用して、ビルダー設計パターンを使用してストリームを作成することもできます。オブジェクトを段階的に構築できるように設計されています。Stream Builderを使用してストリームをインスタンス化する方法を見てみましょう。
Stream.Builder<Integer> numBuilder = Stream.builder();

numBuilder.add(1).add(2).add( 3);

Stream<Integer> numStream = numBuilder.build();
このコードを使用すると、 int要素を含むnumStream という名前のストリームを作成できます。最初に作成されるnumBuilderというStream.Builderインスタンスのおかげで、すべてが非常に迅速に行われます。

3. Stream.of() メソッドを使用して、指定された値でストリームを作成します

ストリームを作成するもう 1 つの方法には、Stream.of()メソッドを使用する方法があります。これは、指定された値でストリームを作成する簡単な方法です。スレッドを宣言し、初期化も行います。Stream.of()メソッドを使用してストリームを作成する例:
Stream<Integer> numStream = Stream.of(1, 2, 3);
このコードは、 Stream.Builderを使用した前のメソッドで行ったのと同じように、int要素 を含むストリームを作成します。ここでは、事前定義された値[1、2、および 3]を持つStream.of()を使用してストリームを直接作成しました。

4. Arrays.stream() メソッドを使用して、既存の配列からストリームを作成します

スレッドを作成するもう 1 つの一般的な方法には、Java で配列を使用する方法があります。ここでのストリームは、Arrays.stream()メソッドを使用して既存の配列から作成されます。すべての配列要素はストリーム要素に変換されます。良い例を次に示します。
Integer[] arr = {1, 2, 3, 4, 5};

Stream<Integer> numStream = Arrays.stream(arr);
このコードは、整数配列である arr という配列の内容を含む numStream を生成します。

5. Stream.concat() メソッドを使用した 2 つの既存のストリームのマージ

ストリームの作成に使用できるもう 1 つのメソッドは、Stream.concat()メソッドです。2 つのスレッドを結合して 1 つのスレッドを作成するために使用されます。両方のストリームが順番に結合されます。これは、最初のスレッドが最初に来て、次に 2 番目のスレッドが続くということを意味します。このような連結の例は次のようになります。
Stream<Integer> numStream1 = Stream.of(1, 2, 3, 4, 5);

Stream<Integer> numStream2 = Stream.of(1, 2, 3);

Stream<Integer> combinedStream = Stream.concat( numStream1, numStream2);
上記のステートメントは、最初のストリームnumStream1と 2 番目のストリームnumStream2の要素を 1 つずつ含む、 combineStream という名前の最終ストリームを作成します。

Java Streamでの操作の種類

すでに述べたように、Java 8 の Java Stream では、中間操作と端末操作という 2 種類の操作を実行できます。それぞれを詳しく見てみましょう。

中間操作

中間操作は出力ストリームを生成し、終了操作が発生した場合にのみ実行されます。これは、中間操作が遅延してパイプライン処理され、端末操作によってのみ完了できることを意味します。遅延評価とパイプラインについては後で学びます。中間操作の例としては、filter()map()Different()Peak()sorted()などのメソッドがあります。

端末の操作

ターミナル操作は中間操作の実行を完了し、出力ストリームの最終結果も返します。端末操作は遅延実行とパイプラインの終了を通知するため、このスレッドは端末操作が完了した後に再度使用することはできません。端末操作の例としては、forEach()collect()count()reduce()などのメソッドがあります。

Java Streamでの操作例

中間操作

Java Stream に適用できる中間操作の例をいくつか示します。

フィルター()

このメソッドは、Java の特定の述語に一致するストリームから要素をフィルタリングするために使用されます。これらのフィルタリングされたアイテムは、新しいストリームを構成します。理解を深めるために例を見てみましょう。
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> even = numStream.filter(n -> n % 2 == 0) .collect(Collectors.toList()); System.out.println(even);
結論:
[98]
説明: この例では、偶数要素 (2 で割り切れる) がfilter()メソッドを使用してフィルタリングされ、整数リストnumStreamに格納され、その内容が後で出力されることが わかります。98 はストリーム内の唯一の偶数整数であるため、出力に表示されます。

地図()

このメソッドは、元の入力ストリームの要素に対してマップされた関数を実行することにより、新しいストリームを作成するために使用されます。おそらく、新しいストリームのデータ型は異なります。例は次のようになります。
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> d = numStream.map(n -> n*2) .collect(Collectors.toList()); System.out.println(d);
結論:
[86、130、2、196、126]
説明: ここでは、 map()メソッドがnumStreamストリームの各要素を単純に 2 倍にするために使用されていること がわかります。出力からわかるように、ストリーム内の各要素は正常に 2 倍化されています。

明確な()

このメソッドは、重複を除外してストリーム内の個々の要素のみを取得するために使用されます。同じ例は次のようになります。
Stream<Integer> numStream = Stream.of(43,65,1,98,63,63,1); List<Integer> numList = numStream.distinct() .collect(Collectors.toList()); System.out.println(numList);
結論:
[43、65、1、98、63]
説明: この場合、numStreamにはDifferent()メソッドが使用されます。ストリームから重複を削除することで、 numList 内のすべての個々の要素を取得します。出力からわかるように、最初に 2 つの重複 (63 と 1) があった入力ストリームとは異なり、重複はありません。

ピーク()

このメソッドは、端末操作を実行する前に中間の変更を追跡するために使用されます。これは、peek() を使用してストリームの各要素に対して操作を実行し、さらに中間操作を実行できるストリームを作成できることを意味します。
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> nList = numStream.map(n -> n*10) .peek(n->System.out.println("Mapped: "+ n)) .collect(Collectors.toList()); System.out.println(nList);
結論:
マッピング済み: 430 マッピング済み: 650 マッピング済み: 10 マッピング済み: 980 マッピング済み: 630 [430, 650, 10, 980, 630]
説明: ここでは、map()メソッドがストリームの要素に適用されるときに、 peek()メソッドを使用して中間結果を生成します。ここで、 collect()端末操作を使用してprintステートメントのリストの最終内容を出力する前でも、各ストリーム要素のマッピングの結果が事前に順次出力されていること がわかります。

ソート済み()

sorted()メソッドは、ストリームの要素をソートするために使用されます。デフォルトでは、要素は昇順で並べ替えられます。特定の並べ替え順序をパラメータとして指定することもできます。このメソッドの実装例は次のようになります。
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); numStream.sorted().forEach(n -> System.out.println(n));
結論:
1 43​​ 63 65 98
説明: ここでは、sorted()メソッドを使用して、デフォルトでストリームの要素を昇順にソートします (特定の順序が指定されていないため)。出力に表示される項目が昇順に並んでいることがわかります。

端末の操作

Java ストリームに適用できる端末操作の例をいくつか示します。

forEach()

forEach()メソッドは、ストリームのすべての要素を反復処理し、各要素に対して関数を 1 つずつ実行するために使用されます。これは、 forwhileなどのループ ステートメントの代替として機能します。例:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); numStream.forEach(n -> System.out.println(n));
結論:
43 65 1 98 63
説明: ここでは、forEach()メソッドを使用して、ストリームの各要素を 1 つずつ出力します。

カウント()

count()メソッドは、ストリーム内に存在する要素の総数を取得するために使用されます。これは、コレクション内の要素の合計数を決定するためによく使用されるsize()メソッドに似ています。Java Stream で count()メソッドを使用する例は次のとおりです。
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); System.out.println(numStream.count());
結論:
5
説明: numStream には5 つの整数要素が含まれているため、それにcount()メソッドを使用すると5 が出力されます。

集める()

collect()メソッドは、ストリーム要素の可変削減を実行するために使用されます。処理の完了後にストリームからコンテンツを削除するために使用できます。Collectorクラスを使用してリダクションを実行します。
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> odd = numStream.filter(n -> n % 2 == 1) .collect(Collectors.toList()); System.out.println(odd);
結論:
[43、65、1、63]
説明: この例では、ストリーム内のすべての奇数要素がフィルタリングされ、oddという名前のリストに収集/縮小されます。最後に、奇数のリストが出力されます。

min()max()

min()メソッドは、名前が示すように、ストリームで使用して、ストリーム内の最小要素を見つけることができます。同様に、max()メソッドを使用して、ストリーム内の最大要素を見つけることができます。例を使用して、それらがどのように使用できるかを理解してみましょう。
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); int smallest = numStream.min((m, n) -> Integer.compare(m, n)).get(); System.out.println("Smallest element: " + smallest);
numStream = Stream.of(43, 65, 1, 98, 63); int largest = numStream.max((m, n) -> Integer.compare(m, n)).get(); System.out.println("Largest element: " + largest);
結論:
最小要素: 1 最大要素: 98
説明: この例では、 min ()メソッドを使用して numStream 内の最小の要素を出力し、 max()メソッドを使用して最大の要素を出力しました。ここでは、max()メソッドを使用する前に、要素を再度numStreamに追加していることに注意してください。これは、min()が終了操作であり、元のストリームの内容を破棄し、最終結果 (この場合は整数「最小」) のみを返すためです。

findAny()findFirst()

findAny() は、ストリームの任意の要素をOptionalとして返します。ストリームが空の場合は、空の Optional value も返します。findFirst() は、ストリームの最初の要素をOptionalとして返します。findAny()メソッドと同様に、対応するストリームが空の場合、findFirst()メソッドも空のOptionalパラメータを返します。これらの方法に基づいて次の例を見てみましょう。
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); Optional<Integer> opt = numStream.findFirst();System.out.println(opt); numStream = Stream.empty(); opt = numStream.findAny();System.out.println(opt);
結論:
オプション[43] オプション.empty
説明: ここで、最初のケースでは、findFirst()メソッドはストリームの最初の要素をOptionalとして返します。その後、スレッドが空のスレッドとして再割り当てされると、findAny()メソッドは空のOptionalを返します。

allMatch()anyMatch()、およびnoneMatch()

allMatch()メソッドは、ストリーム内のすべての要素が特定の述語に一致するかどうかを確認するために使用され、一致する場合はブール値trueを返し、一致しない場合はfalseを返します。ストリームが空の場合はtrueを返します。anyMatch()メソッドは、ストリーム内の要素のいずれかが特定の述語に一致するかどうかを確認するために使用されます。そうであればtrueを返し、そうでない場合はfalse を返します。ストリームが空の場合は、falseを返します。noneMatch()メソッドは、ストリーム内の要素が述語に一致しない場合はtrueを返し、それ以外の場合はfalse を返します。これを説明する例は次のようになります。
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); boolean flag = numStream.allMatch(n -> n1); System.out.println(flag); numStream = Stream.of(43, 65, 1, 98, 63); flag = numStream.anyMatch(n -> n1); System.out.println(flag); numStream = Stream.of(43, 65, 1, 98, 63); flag = numStream.noneMatch(n -> n==1);System.out.println(flag);
結論:
偽真偽
説明:要素として 1 を含む ストリームnumStreamの場合、すべての要素が 1 ではなく、そのうちの 1 つだけが 1 であるため、allMatch()メソッドはfalseを返します。anyMatch()メソッドは、少なくとも 1 つの要素が 1 であるため、trueを返します。noneMatch()メソッドは、このストリームの要素として 1 が実際に存在するため、 falseを返します。

Java Stream での遅延評価

遅延評価は、Java 8 で Java Streams を操作する際の最適化につながります。主に、終了操作が発生するまで中間操作を遅延させることが含まれます。遅延評価は、結果が実際に必要になるまで、計算によるリソースの不必要な浪費を防ぐ役割を果たします。中間操作の結果として得られる出力ストリームは、終了操作が完了した後にのみ生成されます。遅延評価は、Java ストリームのすべての中間操作で機能します。遅延評価は、無限ストリームを操作するときに非常に便利に使用できます。このようにして、多くの不要な処理が防止されます。

Java Stream のパイプライン

Java Stream のパイプラインは、入力ストリーム、次々に並ぶ 0 個以上の中間操作、そして最後に終了操作で構成されます。Java Streams の中間操作は遅延して実行されます。これにより、パイプライン化された中間操作が避けられなくなります。基本的に中間操作を順番に組み合わせたパイプラインを使用すると、遅延実行が可能になります。パイプラインは、最終操作が最終的に発生した後に実行する必要がある中間操作を追跡するのに役立ちます。

結論

それでは、今日学んだことをまとめてみましょう。記事上で:
  1. Java Streams とは何かを簡単に説明しました。
  2. 次に、Java 8 で Java スレッドを作成するためのさまざまなテクニックを学びました。
  3. Java ストリームに対して実行できる 2 つの主なタイプの操作 (中間操作と端末操作) を学習しました。
  4. 次に、中間操作と最終操作の両方のいくつかの例を詳しく調べました。
  5. 結局、遅延評価についてさらに学習し、最後に Java スレッドでのパイプラインについて学習しました。
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION