これはなんですか
streamを知らぬ幼きものたちへのアドバイス
これさえ覚えておけばテストで100点・・はとれないけど、60点くらいはなんとかなるかもしれない。
そもそも
Javaでfor文を使うのは、主に配列として存在する変数を参照し、加工もしくは別な結果を生成するために用いるためです。
たとえばこんなかんじ。
1文字ずつ
String str = "ABCDE";
for (i=0; i<str.length; i++){}
for (byte b: str.getBytes()){}
listとか
List<Character> list = List.of('A', 'B', 'C', 'D', 'E');
for (char c: list) {}
map
Map map = new HashMap() {
{
put("key1", "A");
put("key2", "B");
put("key3", "C");
put("key4", "D");
put("key5", "E");
}
};
// key, valueをセットで扱いたい
for (java.util.Map.Entry entry: map.entrySet()) {
entry.getKey();
entry.getValue();
}
// valueしかいらない
for (String value: map.getValues()) {
...
}
for は必ず主体となる変数があって、その中身を覗くループになる。配列駆動。
whileは主体となる変数は無く、存在するのは条件だけである。条件駆動。
forで書けるものが、なぜstream構文になるのか
forの欠点。
- forはforの外にある変数がインプットとなることが多く、そこへのアクセスを間違いやすいため、知らぬ間にバグが忍び寄る。
- カウンタの使い方間違えたりとか、変なインスタンスを参照したりとかしてバグ。
- 複雑なループになるとループの中で何をしているか知りたい、ループのすべてを俯瞰する必要があって、可読性が。
それらをそこそこ解決するのが stream 構文
streamの歴史
そこそこ古くJava8から利用可能。が、使っている人はあまり居ない。ものによってはプロジェクトで使うことを禁止しているところもある。
後学のため理由を拝聴いたしますと。
「stream構文を読めない人がいるから!」
とのことでした。
yamamotodinはその言葉を発した人をまじまじと見つめ、こう思いました。
『ここでいう人とは21世紀に生きるホモサピエンスだと思うが・・こわ、関わらんとこ』
streamって何
私なりの解釈だと「for文をギュッとして上から下じゃなくて左から右に読んでくようにしたもの」です。
つまり、こういうこと。
な
の
で
す
これが
なのです
こう。
場合によっては
なの
です
javaのCollectionあたりに原初から備わる iterator
の発展型かな、と思っています。
流行り廃りがあるように、最近ではとんと見なくなったものです。
では具体的に何するものか、実際のコードと比較して見てみましょう。
全部回す
List<String> list = Arrays.asList(45, 27, 11, 8, 1);
この配列を回すときはこう(forEach())。
// for
for (int i: list) {}
// stream
list.forEach(i -> {});
forEachはループ全部回したいときに使います。
この場合はあまりfor文と差がありません。が、この中で偶数の数値だけ引っ張りたいとします。
その場合は filter()
// for
for (int i: list) {
if (i%2 == 0) {
}
}
// stream
list.stream().filter(i -> i%2 == 0).map(i -> {});
また、配列から別な配列またはインスタンスへの変換もよく使います。
配列の中身を合計したい。
List<String> intlist = Arrays.asList(45, 27, 11, 8, 1);
int sum = 0;
for (int i: intList) {
sum += i;
}
sum = intList.stream().mapToInt(i -> i).sum();
さて、次が一番よく使うパターン。
配列から別な配列を生み出したい。
たとえば、listから20以上の数字を引っ張り出して文字列にした上で別な配列として返す
List<String> list = List.of(45, 27, 11, 8, 1);
// for
List<String> result = new ArrayList<>();
for (int i: intList) {
if (20 <= i) {
result.add(String.valueOf(i));
}
}
// stream
intList.stream().filter(i -> 20 <= i).map(i -> String.valueOf(i)).collect(Collectors.toList());
やってること。
- filter 20以上の数値群を右側のmapに渡す
- map intをStringに変換する
- collect 返す型を指定する Mapとかにもできる。
とりあえず、このくらいを直感で処理できるようになったら、次のもうちょっと複雑なstreamに進みましょう。
この先はかすれていて読めない。