JavaのStream APIは、ひとことで言うと、Listなどのデータを「順番に加工する流れ」として書くための仕組みです。
たとえば、名簿の中から「Sで始まる名前だけ」を取り出して、「大文字」に変えて、「新しいList」にしたいとします。このような処理を、上から順番に読める形で書けるのがStream APIです。

最初から難しい用語を覚える必要はありません。まずは「データを流して、途中で絞り込みや変換をして、最後に結果を受け取る」と考えればOKです。
この記事では、Stream APIの意味、for文との違い、filter・map・collectの読み方を、初心者向けにできるだけかみ砕いて解説します。
なお、この記事で扱うStreamは、java.util.streamのStream APIです。ファイルの読み書きで出てくるInputStreamやOutputStreamとは別物です。ファイル入出力のストリームは、Java.ioを使用したファイルの読み書きで解説しています。
まず結論:Stream APIは「データを順番に加工する書き方」
Stream APIを理解するときは、次の3つだけを先に押さえてください。
この流れをコードで書くと、だいたい次の形になります。
元のデータ.stream()
.途中の処理()
.途中の処理()
.最後に結果を受け取る処理();
専門用語では、途中の処理を「中間操作」、最後の処理を「終端操作」と呼びます。ただ、初心者のうちは、まず「途中の処理」と「最後の処理」と考えれば十分です。

何に困ったときにStream APIを使うの?
Stream APIは、複数のデータをまとめて扱うときによく使います。
たとえば、次のような場面です。
つまりStream APIは、「データの一覧をどう加工するか」を書くときに便利です。
まだListやMapがあやふやな場合は、先にJavaのListやMapの使い方を読むと理解しやすくなります。
まずはこのコードを読めればOK
Stream APIの基本は、次のコードでかなりつかめます。
List<String> result = names.stream()
.filter(name -> name.startsWith("S"))
.map(name -> name.toUpperCase())
.collect(Collectors.toList());
このコードは、次のように読みます。
| コード | やっていること |
|---|---|
names.stream() | namesというListを処理の流れに乗せる |
filter(...) | 条件に合う名前だけ残す |
map(...) | 残った名前を大文字に変える |
collect(...) | 最後にListとして受け取る |
この読み方ができれば、最初の一歩としては十分です。
for文で書く場合と比べてみる
Stream APIでできることは、for文でも書けます。まず、for文で書くと次のようになります。

List<String> result = new ArrayList<>();
for (String name : names) {
if (name.startsWith("S")) {
result.add(name.toUpperCase());
}
}
これはこれで分かりやすい書き方です。ただし、処理の流れを読むには、resultを作って、for文で回して、if文で判定して、addする、という手順を追う必要があります。
同じ処理をStream APIで書くと、次のようになります。
List<String> result = names.stream()
.filter(name -> name.startsWith("S"))
.map(name -> name.toUpperCase())
.collect(Collectors.toList());
Stream APIでは、「何をしたいのか」が上から順番に並ぶため、慣れると処理の目的を読み取りやすくなります。

Stream APIは「for文より偉い書き方」ではありません。データを絞り込み、変換し、結果を作る流れを見せたいときに便利な書き方です。
filter・map・collectを1つずつ理解する
初心者が最初に覚えるべきなのは、filter・map・collectの3つです。

filterは「条件に合うものだけ残す」
filterは、条件に合うデータだけを残す処理です。
.filter(name -> name.startsWith("S"))
この例では、「Sで始まる名前だけ残す」という意味です。条件に合わないTanakaのような名前は、次の処理へ進みません。
mapは「別の形に変える」
mapは、データを別の形に変える処理です。
.map(name -> name.toUpperCase())
この例では、名前を大文字に変えています。SatoはSATOになります。
collectは「最後に結果を受け取る」
collectは、途中で処理した結果を最後に受け取るための処理です。
.collect(Collectors.toList())
この例では、処理結果を新しいListとして受け取っています。
ここで出てくるname -> ...の部分はラムダ式です。ラムダ式が分からない場合は、Javaのラムダ式の記事をあわせて読むと理解しやすくなります。
実際に動くサンプルコード
全体のコードは次の通りです。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamSample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Sato", "Suzuki", "Tanaka", "Sasaki");
List<String> result = names.stream()
.filter(name -> name.startsWith("S"))
.map(name -> name.toUpperCase())
.collect(Collectors.toList());
System.out.println(result);
}
}
実行すると、次のように表示されます。
[SATO, SUZUKI, SASAKI]
TanakaはSで始まらないため、filterの時点で除外されています。
初心者がつまずきやすいポイント
Stream APIで最初につまずきやすいポイントを整理します。
| つまずき | まずこう理解する |
|---|---|
| StreamはListなの? | Listそのものではなく、Listを処理するための流れ |
| filterだけで結果が出る? | 最後にcollectやcountなどが必要 |
| mapとforEachは同じ? | mapは変換、forEachは最後に1件ずつ処理 |
| parallelStreamは速い? | 必ず速いわけではない。最初はstreamで十分 |
特に大事なのは、Streamは元のListを直接書き換えるものではないという点です。
次のコードでは、大文字にした結果はresultに入ります。元のnamesがそのまま大文字に変わるわけではありません。
List<String> result = names.stream()
.map(name -> name.toUpperCase())
.collect(Collectors.toList());
また、findFirst()のようなメソッドではOptionalが出てくることがあります。Optionalについては、JavaのOptionalとは?で解説しています。
Stream APIはいつ使う?いつ使わない?
Stream APIは、次のような処理に向いています。
一方で、処理の途中で複雑な分岐が何度も出てくる場合や、for文の方が素直に読める場合は、無理にStream APIを使う必要はありません。
Stream APIは、コードを短くするためだけの道具ではありません。データをどう加工して、最後に何を得たいのかを見やすくするための道具です。
関連して読みたい記事
Stream APIは、Javaの他の基礎知識とつながっています。
- Java:ListやMapの使い方を3分で1から解説
- Java:ラムダ式(lambda)を3分でわかりやすく
- Javaの関数型インターフェースを1分でわかりやすく
- JavaのOptionalとは?nullを安全に扱う基本
- Java学習ロードマップ完全版
まとめ
JavaのStream APIは、Listなどのデータを「処理の流れ」として書くための仕組みです。
初心者のうちは、まず次の3つだけ覚えてください。
最初から難しいメソッドを全部覚える必要はありません。まずは、names.stream().filter(...).map(...).collect(...)という流れを読めるようになればOKです。
