【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 件のコメント:
コメントを投稿