カテゴリ: Java 更新日: 2026/03/19

Java Stream APIの基本操作入門|map・filter・forEachを実例で理解

Java Stream APIの基本操作入門|map・filter・forEachを実例で理解
Java Stream APIの基本操作入門|map・filter・forEachを実例で理解

先生と生徒の会話形式で理解しよう

生徒

「Javaでリストのデータを加工したり、特定の条件で絞り込んだりする時、for文ばかり使っていてコードが長くなってしまいます。もっとスッキリ書く方法はありませんか?」

先生

「そんな時に便利なのが『Java Stream API』です。これを使うと、データの集合に対する処理を流れるように、簡潔に記述できるようになりますよ。」

生徒

「Stream APIですか。難しそうなイメージがありますが、私のような初心者でも使いこなせますか?」

先生

「大丈夫です!まずは基本となるmap、filter、forEachの3つをマスターするだけで、コードの見通しが劇的に良くなります。具体的な使い方を一緒に見ていきましょう。」

1. Java Stream APIとは何か?

1. Java Stream APIとは何か?
1. Java Stream APIとは何か?

JavaのStream APIは、Java 8から導入された機能で、配列やリストといったデータの集合(コレクション)を効率的に処理するための仕組みです。これまでのJavaでは、データの抽出や加工を行う際にfor文やif文を組み合わせて記述するのが一般的でしたが、Stream APIを使うことで「何をしたいか」を宣言的に記述できるようになります。

ストリーム(Stream)という名前の通り、データが川の流れのように次々と処理されていくイメージを持つと理解しやすくなります。このAPIの最大の特徴は、元のデータを直接書き換えるのではなく、新しい流れの中で加工を行い、結果を出力する点にあります。これにより、バグが入り込みにくい、安全で読みやすいコードを書くことが可能になります。モダンなJava開発現場では必須の知識となっており、関数型プログラミングの考え方を取り入れた強力なツールです。

2. Stream APIの基本的な処理の流れ

2. Stream APIの基本的な処理の流れ
2. Stream APIの基本的な処理の流れ

Stream APIを利用した処理は、大きく分けて「生成」「中間操作」「終端操作」という3つのステップで構成されます。この流れを理解することが、習得への第一歩です。

  • 生成: リストや配列からストリームを作成します。
  • 中間操作: データの絞り込み(filter)や変換(map)を行います。中間操作は連結することが可能です。
  • 終端操作: 最終的な結果を取り出したり、表示(forEach)したりします。終端操作を実行すると、そのストリームは閉じられます。

例えば、名簿の中から「特定の苗字の人だけを抽出し(中間操作)、名前を敬称付きに変えて(中間操作)、画面に表示する(終端操作)」といった一連の動作を、一つの「パイプライン」として記述できます。この直感的な操作感がStream APIの醍醐味です。従来のループ処理では何行にもわたっていたコードが、わずか数行に凝縮される様子は、多くのエンジニアを驚かせました。まずは、最も頻繁に使われる3つのメソッド、filter、map、forEachから学んでいきましょう。

3. filterメソッドでデータを絞り込む方法

3. filterメソッドでデータを絞り込む方法
3. filterメソッドでデータを絞り込む方法

filterは、その名の通りデータを「濾過(ろか)」するための中間操作です。指定した条件に一致する要素だけを次の処理へ渡します。引数には「述語(Predicate)」と呼ばれる、真偽値(boolean)を返す条件式を渡します。

例えば、数値のリストから偶数だけを取り出したい場合や、文字列のリストから特定の文字数以上の単語だけを抽出したい場合に非常に便利です。条件に合わないデータはそこで捨てられるため、後続の処理が軽くなるというメリットもあります。実際の開発では、データベースから取得した大量のリストから、有効なデータだけを抽出する際によく利用されます。


import java.util.Arrays;
import java.util.List;

public class StreamFilterExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Tanaka", "Sato", "Suzuki", "Ito", "Watanabe");

        // 文字数が5文字より多い名前だけを抽出して表示
        names.stream()
             .filter(name -> name.length() > 5)
             .forEach(System.out::println);
    }
}

実行結果は以下の通りです。条件に合致した「Tanaka」や「Suzuki」、「Watanabe」が表示されます。


Tanaka
Suzuki
Watanabe

4. mapメソッドでデータを変換・加工する

4. mapメソッドでデータを変換・加工する
4. mapメソッドでデータを変換・加工する

mapは、ストリーム内の各要素を別の形に変換するための中間操作です。例えば、文字列を全て大文字に変換したり、数値のリストを2倍にしたり、あるいは特定のオブジェクトから特定のフィールド(名前など)だけを取り出したりする場合に使用します。

データそのものを変質させるのではなく、新しい値としてマッピングするのが特徴です。元のデータ型とは異なる型に変換することも可能です。例えば、ユーザーオブジェクトのリストをmapに通して、メールアドレスの文字列リストに変換するといった使い方が一般的です。これにより、複雑なデータ構造から必要な情報だけをスマートに取り出すことができます。


import java.util.Arrays;
import java.util.List;

public class StreamMapExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // 各数値を10倍に変換して表示
        numbers.stream()
               .map(n -> n * 10)
               .forEach(System.out::println);
    }
}

実行結果は以下のようになります。元のリストの内容が10倍に加工されていることがわかります。


10
20
30
40
50

5. forEachメソッドで結果を出力する

5. forEachメソッドで結果を出力する
5. forEachメソッドで結果を出力する

forEachは、ストリームの各要素に対して指定された処理を実行する終端操作です。最も一般的な使い道は、計算結果や加工済みのデータをコンソールに表示することです。終端操作であるため、これ以降にストリームのメソッドをつなげることはできません。

ラムダ式を使って記述するのが基本で、System.out::printlnのようなメソッド参照を使うと、さらに簡潔に書くことができます。注意点として、forEachの中で副作用(外部の変数を書き換えるなど)を起こすことは、並行処理を行う際などに予期せぬ不具合の原因となるため、推奨されない場合があります。基本的には「結果を確認するための出力用」として使うのがベストプラクティスです。データの流れの最終地点として、処理を締めくくる役割を担っています。

6. filterとmapを組み合わせた実践的な使い方

6. filterとmapを組み合わせた実践的な使い方
6. filterとmapを組み合わせた実践的な使い方

Stream APIの真価は、複数の中間操作を組み合わせた時に発揮されます。条件で絞り込んだ後に変換を行う、という一連の動作を1つのパイプラインで記述してみましょう。以下のサンプルでは、数値リストから3以上の数値だけを選び出し、それを2倍にした結果を表示します。

このように処理を繋げることを「メソッドチェーン」と呼びます。上から下へ、あるいは左から右へ流れるようにコードを読めるため、ロジックの把握が非常に容易になります。従来のfor文の中にif文をネストさせる書き方に比べて、インデントが深くならず、コードの保守性が向上します。


import java.util.Arrays;
import java.util.List;

public class StreamCombinedExample {
    public static void main(String[] args) {
        List<Integer> scoreList = Arrays.asList(10, 45, 60, 30, 85, 90);

        // 50点以上のスコアだけを抽出し、「点数は〇〇です」という文字列に変換して出力
        scoreList.stream()
                 .filter(s -> s >= 50)
                 .map(s -> "合格!点数は" + s + "です")
                 .forEach(System.out::println);
    }
}

実行結果は以下の通りです。フィルタリングとマッピングが同時に行われています。


合格!点数は60です
合格!点数は85です
合格!点数は90です

7. ラムダ式とメソッド参照の基礎知識

7. ラムダ式とメソッド参照の基礎知識
7. ラムダ式とメソッド参照の基礎知識

Stream APIを使いこなす上で欠かせないのが「ラムダ式」です。ラムダ式とは、メソッドを変数のように扱える仕組みのことです。例えば、n -> n * 2という記述は、「引数nを受け取って、nに2を掛けた結果を返す」という匿名の関数を表しています。これにより、わざわざ別の場所にメソッドを定義しなくても、その場でサッと処理内容を記述できるのです。

また、さらに省略した書き方として「メソッド参照」があります。先ほど登場したSystem.out::printlnがその代表例です。これは「引数をそのままprintlnメソッドに渡す」という動作を意味します。ラムダ式とメソッド参照を適切に使い分けることで、Javaのコードは驚くほど短く、そして読みやすくなります。初心者の方は、まずは「->」という記号を見たら「左側が入力、右側が処理内容」だと考えるとスムーズに馴染めるはずです。

8. コレクションへの変換 collectメソッドの活用

8. コレクションへの変換 collectメソッドの活用
8. コレクションへの変換 collectメソッドの活用

ここまではforEachで出力する例を見てきましたが、実務では「加工した結果を再びListとして受け取りたい」という場面が多いです。そんな時に使うのがcollectという終端操作です。Collectors.toList()と組み合わせることで、ストリームの処理結果を新しいリストに格納できます。

これを使えば、元のリストを汚さずに、必要なデータだけを抽出・加工した「加工済みリスト」を簡単に作成できます。例えば、特定のユーザー属性を持つ人たちの名前だけを抜き出して、別のメソッドに引数として渡したい時などに非常に重宝します。Stream APIは単なる出力ツールではなく、データの「再構築」に非常に強力な力を発揮します。


import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamCollectExample {
    public static void main(String[] args) {
        List<String> fruits = Arrays.asList("apple", "banana", "cherry", "dragonfruit");

        // 'a'を含むフルーツだけを抽出して新しいリストを作成
        List<String> filteredFruits = fruits.stream()
                                            .filter(f -> f.contains("a"))
                                            .collect(Collectors.toList());

        // 結果のリストを表示
        System.out.println("抽出されたリスト: " + filteredFruits);
    }
}

実行結果は以下のようになります。新しいリストとしてデータが保持されていることが確認できます。


抽出されたリスト: [apple, banana, dragonfruit]

9. Stream APIを使う際の注意点とデバッグ

9. Stream APIを使う際の注意点とデバッグ
9. Stream APIを使う際の注意点とデバッグ

非常に便利なStream APIですが、いくつか注意点があります。まず、ストリームは「一度しか使えない」という点です。一度終端操作を行ったストリームを再利用しようとすると、例外が発生します。また、あまりにも複雑な処理を一つのメソッドチェーンに詰め込みすぎると、逆に可読性が落ちてしまうことがあります。適度な改行や、時には伝統的なfor文の方が読みやすい場合もあることを覚えておきましょう。

デバッグについては、EclipseやIntelliJ IDEAといった開発環境の機能を活用するのがおすすめです。ストリームの途中の状態を可視化するツールも存在します。また、途中のデータを確認したい場合は、peekメソッドを使うことで、ストリームの流れを止めずに中身を覗き見ることができます。これらを駆使して、安全かつ効率的なコーディングを目指しましょう。Javaのスキルアップにおいて、Stream APIの習得は避けては通れない、非常に価値のあるステップです。

カテゴリの一覧へ
新着記事
New1
Micronaut
Micronautの@Factoryとは?複雑なBean生成を管理するための方法を解説
New2
Quarkus
QuarkusのDIとCDIを完全理解!@Producesでプロデューサーメソッドを使う方法を初心者向けに解説
New3
Java
JavaのStringBufferクラスを徹底解説!スレッド安全な文字列操作の仕組みと使い分け
New4
Micronaut
Micronautで非同期HTTP処理を行う方法!リアクティブ対応の基礎知識
人気記事
No.1
Java&Spring記事人気No1
Quarkus
Quarkus入門!GitHub ActionsでCI/CDパイプラインを構築して自動ビルドを実現する方法
No.2
Java&Spring記事人気No2
Java
Javaのコンパイルと実行の流れを解説!JVM・JDK・JREの違いも初心者向けに整理
No.3
Java&Spring記事人気No3
Micronaut
Micronautのルーティング設定ガイド!プレフィックス付与とAPIバージョニングの基本
No.4
Java&Spring記事人気No4
Quarkus
QuarkusのCI/CD入門!GitHub Actionsで自動デプロイを実現する方法
No.5
Java&Spring記事人気No5
Micronaut
Micronautのフィルタ徹底解説!HTTPリクエスト共通処理をスマートに追加する方法
No.6
Java&Spring記事人気No6
Java
Java Optional ifPresentの使い方を徹底解説!nullチェックをスマートに省略する方法
No.7
Java&Spring記事人気No7
Java
Java Functionインタフェースの使い方を完全ガイド!map変換と処理チェーンを理解する
No.8
Java&Spring記事人気No8
Java
JavaのString比較を徹底解説!equalsと==の違い、初心者が陥る罠とは?