技術共有

Java Stream APIの詳細説明: 収集データを効率的に処理するための強力なツール

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

導入

Java 8 では多くの新機能が導入されており、その中で最も注目に値するのは Lambda 式と Stream API です。 Stream API は、収集データを処理するための効率的かつ簡潔な方法を提供し、コードをより簡潔かつ明確にし、可読性と保守性を高めます。この記事では、基本概念、一般的な操作、並列処理、実際のケース、ベスト プラクティスなど、Java Stream API の使用法について詳しく説明します。

目次

  1. ストリームAPIとは
  2. ストリームAPIの基本操作
  3. Stream APIの高度な操作
  4. パラレルストリーム
  5. ストリームAPIの実践例
  6. ストリーム API のベスト プラクティス
  7. よくある質問と解決策
  8. 要約する

ストリームAPIとは

Stream API は、コレクション データを処理するために Java 8 で導入された抽象化であり、(SQL ステートメントと同様の) 宣言的な方法でデータを処理できるようになります。 Stream API は、コレクションに対するフィルター、並べ替え、マッピング、リデュースなどの操作に使用できる多くの強力な操作を提供し、コードを大幅に簡素化します。

特徴

  • 宣言型プログラミング: Stream API を使用して宣言的な方法でコードを記述し、定型的なコードを削減します。
  • チェーンコール: Stream API の操作をチェーンで呼び出すことができるため、コードの可読性が向上します。
  • 遅延評価: 中間操作は遅延評価され、終了操作が実行されるときにのみ評価されます。
  • 並列処理: 並列処理をサポートし、マルチコア CPU を最大限に活用できます。

ストリームAPIの基本操作

ストリームの作成

ストリーム API には、ストリームを作成するためのさまざまな方法が用意されており、一般的なものには次のようなものがあります。

  1. コレクションから作成する
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
  • 1
  • 2
  1. 配列から作成
String[] array = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(array);
  • 1
  • 2
  1. 使用Stream.of
Stream<String> stream = Stream.of("a", "b", "c");
  • 1
  1. 使用Stream.generate
Stream<Double> stream = Stream.generate(Math::random).limit(10);
  • 1
  1. 使用Stream.iterate
Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(10);
  • 1

中間操作

中間操作はストリームの変換に使用され、遅延評価されます。一般的な中間操作には次のようなものがあります。

  1. filter: 要素をフィルタリングするために使用されます。
Stream<String> stream = list.stream().filter(s -> s.startsWith("a"));
  • 1
  1. map: 各要素を対応する結果にマッピングするために使用されます。
Stream<String> stream = list.stream().map(String::toUpperCase);
  • 1
  1. flatMap: 各要素をストリームに変換し、それを 1 つのストリームにマージするために使用されます。
Stream<String> stream = list.stream().flatMap(s -> Stream.of(s.split("")));
  • 1
  1. distinct: 重複を削除するために使用されます。
Stream<String> stream = list.stream().distinct();
  • 1
  1. sorted: 並べ替えに使用されます。
Stream<String> stream = list.stream().sorted();
  • 1
  1. peek: 処理中に各要素を表示するために使用されます。
Stream<String> stream = list.stream().peek(System.out::println);
  • 1

端末操作

ターミナル操作は、ストリームの計算を開始し、結果を生成するために使用されます。一般的なターミナル操作には次のものがあります。

  1. forEach: 各要素に対して操作を実行します。
list.stream().forEach(System.out::println);
  • 1
  1. collect: ストリームを他の形式に変換します。
List<String> result = list.stream().collect(Collectors.toList());
  • 1
  1. reduce: ストリーム内の要素を値に還元します。
Optional<String> result = list.stream().reduce((s1, s2) -> s1 + s2);
  • 1
  1. toArray:ストリームを配列に変換します。
String[] array = list.stream().toArray(String[]::new);
  • 1
  1. count:要素数をカウントします。
long count = list.stream().count();
  • 1
  1. anyMatchallMatchnoneMatch:マッチング判定に使用します。
boolean anyMatch = list.stream().anyMatch(s -> s.startsWith("a"));
boolean allMatch = list.stream().allMatch(s -> s.startsWith("a"));
boolean noneMatch = list.stream().noneMatch(s -> s.startsWith("a"));
  • 1
  • 2
  • 3
  1. findFirstfindAny: 要素を検索するために使用されます。
Optional<String> first = list.stream().findFirst();
Optional<String> any = list.stream().findAny();
  • 1
  • 2

Stream APIの高度な操作

選別

使用sortedこのメソッドはストリームを並べ替え、コンパレーターに渡すことができます。

List<String> list = Arrays.asList("b", "c", "a");
List<String> sortedList = list.stream().sorted().collect(Collectors.toList());
// 逆序排序
List<String> sortedListDesc = list.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
  • 1
  • 2
  • 3
  • 4

フィルター

使用filterStream内の要素をフィルタリングするメソッド。

List<String> list = Arrays.asList("a", "b", "c");
List<String> filteredList = list.stream().filter(s -> s.startsWith("a")).collect(Collectors.toList());
  • 1
  • 2

マッピング

使用mapメソッドはストリーム内の要素をマップします。

List<String> list = Arrays.asList("a", "b", "c");
List<String> mappedList = list.stream().map(String::toUpperCase).collect(Collectors.toList());
  • 1
  • 2

法令

使用reduceストリーム内の要素を削減するメソッド。

List<String> list = Arrays.asList("a", "b", "c");
String result = list.stream().reduce("", (s1, s2) -> s1 + s2);
  • 1
  • 2

集める

使用collectメソッドはストリームを他の形式に変換します。

List<String> list = Arrays.asList("a", "b", "c");
List<String> collectedList = list.stream().collect(Collectors.toList());
Set<String> collectedSet = list.stream().collect(Collectors.toSet());
String joinedString = list.stream().collect(Collectors.joining(","));
  • 1
  • 2
  • 3
  • 4

パラレルストリーム

Parallel Stream はマルチコア CPU の利点を最大限に活用し、データ処理効率を向上させることができます。使えるparallelStreamメソッドは並列ストリームを作成します。

List<String> list = Arrays.asList("a", "b", "c");
List<String> parallelList = list.parallelStream().map(String::toUpperCase).collect(Collectors.toList());
  • 1
  • 2

も使用できますparallel通常の Stream を並列 Stream に変換するメソッドです。

List<String> list = Arrays.asList("a", "b", "c");
List<String> parallelList = list.stream().parallel().map(String::toUpperCase).collect(Collectors.toList());
  • 1
  • 2

並列ストリームは常にシリアル ストリームより高速であるとは限らず、特定の状況に応じてテストする必要があることに注意してください。

ストリームAPIの実践例

収集データの処理

ケース 1: コレクションのフィルターと変換

文字列のコレクションが与えられた場合、長さが 3 未満の文字列をフィルターで除外し、残りの文字列を大文字に変換します。

List<String> list = Arrays.asList("a", "ab", "abc", "abcd");
List<String> result = list.stream()
    .filter(s -> s.length() >= 3)
    .map(String::toUpperCase)
    .collect(Collectors.toList());
System.out.println(result); // 输出:[ABC, ABCD]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
ケース 2: 平均を計算する

一連の整数を指定して、すべての整数の平均を計算します。

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
OptionalDouble average = list.stream()
    .mapToInt(Integer::intValue)
    .average();
average.ifPresent(System.out::println); // 输出:3.0
  • 1
  • 2
  • 3
  • 4
  • 5

ファイル操作

ケース 3: ファイルの内容を読み取る

ストリームAPIの使用

ファイルの内容を読み取り、コンソールに出力します。

try (Stream<String> lines = Files.lines(Paths.get("example.txt"))) {
    lines.forEach(System.out::println);
} catch (IOException e) {
    e.printStackTrace();
}
  • 1
  • 2
  • 3
  • 4
  • 5
ケース 4: 単語の出現数を数える

ファイルの内容を読み取り、各単語の出現数を数えます。

try (Stream<String> lines = Files.lines(Paths.get("example.txt"))) {
    Map<String, Long> wordCount = lines
        .flatMap(line -> Arrays.stream(line.split("\W+")))
        .collect(Collectors.groupingBy(String::toLowerCase, Collectors.counting()));
    wordCount.forEach((word, count) -> System.out.println(word + ": " + count));
} catch (IOException e) {
    e.printStackTrace();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

データベース操作

ケース 5: データベース クエリ結果の処理

データベーステーブルがあるとします。users、フィールドを含むidnameそしてage 。 Stream API を使用してクエリ結果を処理できます。

List<User> users = queryDatabase();
List<String> names = users.stream()
    .filter(user -> user.getAge() > 18)
    .map(User::getName)
    .collect(Collectors.toList());
System.out.println(names);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

ストリーム API のベスト プラクティス

  1. 不必要な並列化を避ける: Parallel Stream は常に高速であるとは限らないため、ケースバイケースで選択する必要があります。
  2. 中間操作と末端操作の適切な使用: 中間操作は遅延評価され、終了操作が実行されるときにのみ評価されます。
  3. Stream の再利用性に注意する: ストリームを一度消費すると、再度使用することはできません。再利用する必要がある場合は、使用する前にストリームをコレクションに変換することを検討してください。
  4. 適切なコレクターを使用するCollectorsこのクラスにはさまざまなコレクターが用意されており、特定のニーズに応じて適切なコレクターを選択できます。
  5. 例外を処理する: Stream API を使用する場合、特にファイル操作やデータベース操作で発生する可能性のある例外を処理する必要があります。

よくある質問と解決策

ストリームは閉じられています

ストリームを一度消費すると、再度使用することはできません。再利用する必要がある場合は、使用する前にストリームをコレクションに変換することを検討してください。

List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
stream.forEach(System.out::println);
stream.forEach(System.out::println); // 会抛出IllegalStateException
  • 1
  • 2
  • 3
  • 4

パフォーマンスの問題

並列ストリームは常にシリアル ストリームより高速であるとは限らないため、ケースバイケースでテストする必要があります。使えるForkJoinPool並列ストリームのパフォーマンスを最適化します。

ForkJoinPool customThreadPool = new ForkJoinPool(4);
customThreadPool.submit(() ->
    list.parallelStream().forEach(System.out::println)
).get();
  • 1
  • 2
  • 3
  • 4

メモリーリーク

Stream API を使用して大量のデータを処理する場合は、メモリ リークの問題に注意する必要があります。使えるcloseメソッドを使用してストリームを閉じるか、try-with-resourcesステートメントはストリームを自動的に閉じます。

try (Stream<String> lines = Files.lines(Paths.get("example.txt"))) {
    lines.forEach(System.out::println);
} catch (IOException e) {
    e.printStackTrace();
}
  • 1
  • 2
  • 3
  • 4
  • 5

要約する

この記事では、基本操作、高度な操作、並列処理、実践事例、ベストプラクティスなど、Java Stream API の使い方を詳しく紹介します。 Stream API を合理的に利用することで、開発者はコードを大幅に簡素化し、コードの可読性と保守性を向上させ、データ処理の効率も向上させることができます。この記事が Java 開発における Stream API の使用に役立つことを願っています。

Java Stream API は、収集データを処理するための強力なツールです。さまざまな操作を柔軟に使用することで、効率的なデータ処理とストリーミング コンピューティングを実現できます。 Stream API を使用したことがない場合は、できるだけ早くこの強力なツールを学習してマスターし、プロジェクトに適用して開発効率とコードの品質を向上させることをお勧めします。