2026年2月15日日曜日

【Java進化史 第14回】:Java8 〜若手のこの1行が読めない。Streamの衝撃〜(理屈編)

【Java進化史 第14回】Java8 〜若手のこの1行が読めない。Streamの衝撃〜(理屈編)


■ この記事で分かること

  • なぜStreamが読みにくく感じるのか
  • なぜfor文が見えなくなったのか
  • Streamが自動でやってくれていること
  • 「どうやるか」から「何をするか」への変化

■ 若手のこの1行で、私は止まった


names.stream()
     .filter(n -> n.startsWith("S"))
     .map(String::toUpperCase)
     .sorted()
     .forEach(System.out::println);

どうも、Sで始まる名前を集めて、
大文字にして、並べ替えているらしい。

……らしい。

しかし、どこで回しているのかが見えない。

なんじゃこりゃ。


■ 私が知っているJava(Java7まで)


List<String> result = new ArrayList<>();

for (String name : names) {
    if (name.startsWith("S")) {
        result.add(name.toUpperCase());
    }
}

Collections.sort(result);

for (String name : result) {
    System.out.println(name);
}

こちらは安心する。
回して、条件を見て、入れて、並べ替えて、出力する。
全部、自分でやっている。


■ Streamの見た目の流れ


names
  ↓
stream()
  ↓
filter
  ↓
map
  ↓
sorted
  ↓
forEach

左から順番に実行されているように見える。


■ しかし実際はこう動く


① パイプライン(処理の設計図)を作る
   ├─ filter
   ├─ map
   └─ sorted

② 終端操作(forEach)が呼ばれた瞬間

③ まとめて一気に実行

filterやmapは、書いた瞬間には動いていない。

動くのは、最後のforEachが呼ばれたときだ。


■ Streamはforの書き換えなのか?

Streamは、forを書かなくていい仕組みだ。

でも、回っていないわけではない。
見えないところで、ちゃんと回っている。

forが消えたのではない。
自分で書かなくてよくなっただけだ。


■ さらに違うところ


names.parallelStream()

これだけで並列化できる。

Java7でも並列処理は書けた。
しかし、スレッド管理や同期を自分で制御する必要があった。

Streamは、そこまで抽象化している。


■ 私の誤解

私は最初、「短く書ける便利機能」だと思った。

しかし違った。

これは、ループを書くという作業を、
Javaに預ける仕組みだった。

楽をしているのではない。
抽象度を一段、上げただけだった。


■ まとめ

  • Streamはforを消していない
  • 回す仕組みを内部に隠した
  • 並列処理を簡単に扱えるようにした
  • 「やり方」よりも「意図」を書くようになった

■ 次回予告

Streamは、forの書き換えに見える。
私も最初はそう思った。

回すことを、少し楽にしただけの機能だと。

だが、どうもそれだけではない。

なぜなら、filterやmapは、
書いた瞬間にはまだ動いていないからだ。

では、いつ動いているのか。

次回、この1行を分解してみる。

0 件のコメント:

コメントを投稿

【Java進化史 第18回】Java8 〜「@」も読めなかった日

【Java進化史 第18回】Java8 〜「@」も読めなかった日〜 若手のコードを見たとき、 固まったことがある。 -> が読めない。 Streamも追えない。 そして、やたらと出てくる「@」。 なんじゃこれ? ■ @ は何者か 調べ...