【Java】Stream API 1000本ノック

# 環境
java 17 (amazon corretto)

# filter
リストの中から任意の条件に合致する要素だけをフィルタリングする。

```java
        Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
                .filter(e -> e % 2 == 0)
                .forEach(System.out::println);
```

```
2
4
6
8
10
```

# map
リストの中身を加工して返す。

```java
        Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
                .map(e -> e * 100)
                .forEach(System.out::println);
```

```
100
200
300
400
500
600
700
800
900
1000
```

# mapToInt
Stringのリストをintに変換して返す。
```java
        int[] mappedToInt = Stream.of("1", "2", "3", "4", "5", "6", "7", "8", "9", "10")
                .mapToInt(Integer::parseInt)
                .toArray();
        Arrays.stream(mappedToInt).forEach(System.out::println);
```

```
1
2
3
4
5
6
7
8
9
10
```

mapToIntの戻り値はIntStreamであり、mapの戻り値のStreamとは異なるため、toList()でのリスト変換ができない。
代わりに、
```java
    <R> R collect(Supplier<R> supplier,
                  ObjIntConsumer<R> accumulator,
                  BiConsumer<R, R> combiner);
```
が用意されているので、
```java
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll)
```
とすればリスト変換も可能。

ちょっとめんどくさい。せっかく最近のJavaアプデで`collect(Collectors.toList())` -> `toList()`へと簡略化されたので、IntStreamも対応してほしいところ。

https://stackoverflow.com/questions/50552258/why-cant-maptoint-be-used-with-collecttolist

mapToIntの親戚にmapToLong, mapToDoubleもいる。（割愛）

# flatMap
ネストしたリストをフラット（ネストのない）なリストに変換する。
```java
        var devices = List.of(
                List.of("iPhone13, iPhone13 Pro", "iPhone 13 mini"),
                List.of("Galaxy", "Arrows", "AQUOS"));

        var flatMapped = devices.stream().flatMap(Collection::stream).toList();
        System.out.println(flatMapped.toString()); // [iPhone13, iPhone13 Pro, iPhone 13 mini, Galaxy, Arrows, AQUOS]
```

`var flatMapped = devices.stream().flatMap(Collection::stream).toList();`は、メソッド参照せずに書くと以下。

```java
        var flatMapped = devices.stream().flatMap(device -> device.stream()).toList();
```

他にも↓のような使い方もある。

https://qiita.com/KevinFQ/items/97137efb2159009b60e1

# flatMapToInt
TBD

# distinct
https://qiita.com/Sirloin/items/e6aa4334fba250c738de#streamdistinct

# sorted
https://qiita.com/Sirloin/items/e6aa4334fba250c738de#%E3%82%BD%E3%83%BC%E3%83%88

# peek
```java
        var list = Stream.of("one", "two", "three", "four")
                .filter(e -> e.length() > 3)
                .peek(e -> System.out.println("Filtered value: " + e))
                .map(String::toUpperCase)
                .peek(e -> System.out.println("Mapped value: " + e))
                .toList();
        System.out.println(list);
```

実行結果↓
```
> Task :Main.main()
Filtered value: three
Mapped value: THREE
Filtered value: four
Mapped value: FOUR
[THREE, FOUR]
```

Stream処理のデバッグ用に用意されているっぽい。
実装中のデバッグとして使うのはありだが、プロダクトコードにpeek自体が記載されることはないような気がする。

↑のコードで少し勉強になったのはStreamの処理の順序。
↓のように
```
Filtered value: three
Filtered value: four
Mapped value: THREE
Mapped value: FOUR
```
filter -> mapの順で処理されてると思いこんでいたが、実際は違うのね。

https://docs.oracle.com/javase/jp/8/docs/api/java/util/stream/Stream.html#peek-java.util.function.Consumer-

# limit
```java
        Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).limit(5).forEach(System.out::println);
```

```
1
2
3
4
5
```

リストを任意のサイズに絞ることで、「n番目までの要素」を別リストとして切り出せる。
後述のskipと相性がいい。

# skip
```java
        var list = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        var skipped = list.stream().skip(3).toList();
        System.out.println(skipped);
```

最初のn個をスキップしたリストを返す。
limitと組み合わせることで、柔軟にリストを取り出すことができる。

```java
        Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).skip(3).limit(2).forEach(System.out::println);
```

```
4
5
```

# takeWhile
```java
        Stream.of(1, 2, 3, 1, 2, 3).takeWhile(e -> e < 3).forEach(System.out::println);
```

```
1
2
```

条件に一致する要素だけを取り出す。

collentionの1番目の要素が条件に一致しない場合は、何も取り出されない。
```java
        Stream.of(1, 2, 3, 1, 2, 3).takeWhile(e -> e%2 == 0).forEach(System.out::println);
        // ↑何も出力されない
```

なお、filterと違って、条件に一致しない要素が見つかった時点で走査をやめる。

```java
        var takeWhile = Stream.of(1, 2, 3, 1, 2, 3).takeWhile(e -> e < 3).toList();
        System.out.println(takeWhile); //[1, 2]

        var filtered = Stream.of(1, 2, 3, 1, 2, 3).filter(e -> e < 3).toList();
        System.out.println(filtered); //[1, 2, 1, 2]
```

https://stackoverflow.com/questions/46850689/how-is-takewhile-different-from-filter

# dropWhile
条件に一致する要素を除外する。takeWhileの逆版。

```java
        Stream.of(1, 2, 3, 1, 2, 3).dropWhile(e -> e < 3).forEach(System.out::println);
```

```
3
1
2
3
```

# forEach
Streamを習ったときに恐らく一番最初に知るであろうもの。
この記事でもすでに散々使っている。

```java
        Stream.of("yamada", "suzuki", "watanabe")
                .forEach(e -> {
                    System.out.println("My name is %s".formatted(e));
                });
```

```
My name is yamada
My name is suzuki
My name is watanabe
```

# forEachOrdered
並列ストリームを用いる際に、streamの順番通りに処理したいときに使う。

```java
        IntStream.range(0, 10).forEach(System.out::print);//順次ストリーム
        System.out.println();
        IntStream.range(0, 10).parallel().forEach(System.out::print);//並列ストリーム
        System.out.println();
        IntStream.range(0, 10).parallel().forEachOrdered(System.out::print);//並列ストリーム
```

```
0123456789
6578914302
0123456789
```

＜参考＞

https://qiita.com/nmby/items/52d1b0e2dad5df475737

https://qiita.com/dhirabayashi/items/e9b0d85b33ad04857276

# toArray
StreamからArrayに変換する。引数を省略するとObject[]が返る。
```java
        String[] array = Stream.of("a", "b", "c").toArray(String[]::new);
```

# reduce
https://qiita.com/sparklingbaby/items/f6f3ce92dc13a9d8fc07

この記事がわかりやすかった。
記事にもあるが、
> reduceはStream APIの中でも最も難解な関数のひとつだと思います。mapやfilterは使っているうちに慣れたという人でも、reduceをバリバリ使う人はあまりいないのではないでしょうか。

reduceは非常に難解。
reduceには引数違いの3種類の兄弟がいる。

## `Optional<T> reduce(BinaryOperator<T> accumulator)`
javadocに以下の記載がある。
https://docs.oracle.com/javase/jp/17/docs/api/java.base/java/util/stream/Stream.html#reduce(java.util.function.BinaryOperator)

```java
     boolean foundAny = false;
     T result = null;
     for (T element : this stream) {
         if (!foundAny) {
             foundAny = true;
             result = element;
         }
         else
             result = accumulator.apply(result, element);
     }
     return foundAny ? Optional.of(result) : Optional.empty();
```

※`BinaryOperator<T>`はざっくりいうと`R apply(T, T)`という感じ。

streamに対して引数`accumulator`で指定した畳み込み処理を実施し、結果があればそのOptionalを返す（なければOptional.empty）。

```java
var sum1 = Stream.of(1, 2, 3, 4, 5).reduce((a, b) -> a + b);
// Optional[15]
```

```java
var sum2 = Stream.of(1, 2, 3, 4, 5).filter(e -> e > 10).reduce((a, b) -> a + b);
// Optional.empty
```

## `T reduce(T identity, BinaryOperator<T> accumulator)`
javadocによると、以下と同義。
https://docs.oracle.com/javase/jp/17/docs/api/java.base/java/util/stream/Stream.html#reduce(T,java.util.function.BinaryOperator)

```java
     T result = identity;
     for (T element : this stream)
         result = accumulator.apply(result, element)
     return result;
```

第一引数の`identity`は単位元。

```java
var sum3 = Stream.of(1, 2, 3, 4, 5).reduce(10, (a, b) -> a + b);
// 25
```

前項の`Optional<T> reduce(BinaryOperator<T> accumulator)`した結果が空の場合はOptional.emptyを返していたが、このreduceはからの場合は`identity`を返す。

```java
var sum4 = Stream.of(1, 2, 3, 4, 5).filter(e -> e > 10).reduce(10, (a, b) -> a + b);
// 10
```



## `<U> U reduce(U var1, BiFunction<U, ? super T, U> var2, BinaryOperator<U> var3);`
javadocによると以下と同義。
https://docs.oracle.com/javase/jp/17/docs/api/java.base/java/util/stream/Stream.html#reduce(U,java.util.function.BiFunction,java.util.function.BinaryOperator)

```java
     U result = identity;
     for (T element : this stream)
         result = accumulator.apply(result, element)
     return result;
```
 
> ただし、順次実行の制約が課されるわけではありません。
identity値はコンバイナ関数の単位元でなければいけません。 つまり、すべてのuについて、combiner(identity, u)がuに等しくなります。 さらに、combiner関数はaccumulator関数と互換性がある必要があります。すべてのuとtについて、次が成り立つ必要があります。

```java
     combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t)
```

# collect
## <R, A> R collect(Collector<? super T, A, R> var1);

```java
        List<String> list = Stream.of("a", "b", "c").collect(Collectors.toList());
        Map<String, String> map = Stream.of("a", "b", "c")
                .collect(Collectors.toMap(String::toUpperCase, e -> e));
```

`var1`にCollectorsのメソッドを渡して、ListやMapを生成できる。
だが、1つ目のListは、Java17より

```java
    List<String> list = Stream.of("a", "b", "c").toList();
```
とできるので、こっちをつかったほうがいい。

## <R> R collect(Supplier<R> var1, BiConsumer<R, ? super T> var2, BiConsumer<R, R> var3);
TBD

# min

```java
        var minNum = Stream.of(1, 2, 3, 4, 5).min(Comparator.naturalOrder());
        var minStr = Stream.of("a", "b", "c").min(Comparator.naturalOrder());

        System.out.println(minNum);
        System.out.println(minStr);
```

```
Optional[1]
Optional[A]
```

最小値を取得する。
Stringの場合は辞書順で最も若いものを取得する。

# max

```java
        var maxNum = Stream.of(1, 2, 3, 4, 5).max(Comparator.naturalOrder());
        var maxStr = Stream.of("a", "b", "c").max(Comparator.naturalOrder());

        System.out.println(maxNum);
        System.out.println(maxStr);
```

```
Optional[5]
Optional[c]
```

最大値を取得する。
Stringの場合は辞書順で最も後のものを取得する。

# count

```java
        var count = Stream.of(1, 2, 3, 4, 5).count();
        System.out.println(count);
```

```
5
```

streamの要素の個数を取得する。ただそれだけ。

# anyMatch
```java
        boolean result1 = Stream.of("apple", "baggage", "cake").anyMatch(e -> e.startsWith("a"));
        // true
        boolean result2 = Stream.of("apple", "baggage", "cake").anyMatch(e -> e.startsWith("z"));
        // false
 ```

条件に合致する要素が1個以上あればtrueを返す。

# allMatch
```java
        boolean result = Stream.of("apple", "baggage", "cake").allMatch(e -> e.endsWith("e"));
        // true
        boolean result2 = Stream.of("apple", "baggage", "cake").allMatch(e -> e.startsWith("a"));
        // false
```

全ての要素が条件に合致する場合のみtrueを返す。

# noneMatch
```java
        boolean result = Stream.of("apple", "baggage", "cake").noneMatch(e -> e.startsWith("z"));
        // true
        boolean result2 = Stream.of("apple", "baggage", "cake").noneMatch(e -> e.endsWith("e"));
        // false
```

全ての要素が条件に合致**しない**場合にtrueを返す。

# findFirst
```java
        var e = Stream.of("a", "b", "c").findFirst();
        System.out.println(e);
```

```
Optional[a]
```

streamの中から1番目の要素をOptional型で取り出す。

```java
        var result = Stream.of("a", "b", "c").filter(e -> e.length() > 1).findFirst();
        System.out.println(result);
```

```
Optional.empty
```

結果がなければempty

```java
        System.out.println("findFirst(直列)");
        for (int i = 0; i < 20; i++) {
            System.out.println(Stream.of("a", "b", "c", "d", "e", "f")
                    .filter(e -> e.equals("c"))
                    .findFirst());
        }
        System.out.println("findFirst(並列)");
        for (int i = 0; i < 20; i++) {
            System.out.println(Stream.of("a", "b", "c", "d", "e", "f")
                    .parallel()
                    .filter(e -> e.equals("c"))
                    .findFirst());
        }
```

```
findFirst(直列)
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
findFirst(並列)
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
Optional[c]
```

`findFirst`は直列・並列Streamどちらの場合も、1番目の要素を返す。

# findAny
```java
        var result = Stream.of("a", "b", "c", "d", "e", "f").findAny();
        System.out.println(result);
```

```
Optional[a]
```

streamのなかから任意の要素をOptional型で返す。

```java
        System.out.println("findAny(直列)");
        for (int i = 0; i < 20; i++) {
            System.out.println(Stream.of("a", "b", "c", "d", "e", "f").findAny());
        }
        System.out.println("findAny(並列)");
        for (int i = 0; i < 20; i++) {
            System.out.println(Stream.of("a", "b", "c", "d", "e", "f").parallel().findAny());
        }
```

```
findAny(直列)
Optional[a]
Optional[a]
Optional[a]
Optional[a]
Optional[a]
Optional[a]
Optional[a]
Optional[a]
Optional[a]
Optional[a]
Optional[a]
Optional[a]
Optional[a]
Optional[a]
Optional[a]
Optional[a]
Optional[a]
Optional[a]
Optional[a]
Optional[a]
findAny(並列)
Optional[d]
Optional[d]
Optional[d]
Optional[d]
Optional[d]
Optional[d]
Optional[d]
Optional[d]
Optional[d]
Optional[d]
Optional[d]
Optional[d]
Optional[d]
Optional[d]
Optional[b]
Optional[d]
Optional[d]
Optional[d]
Optional[d]
Optional[d]
```

findAnyはfindFirstより高速だが、並列ストリームで実行した場合に返却される要素がランダムになる。

# builder
```java
        Stream.builder().add(Arrays.asList("a", "b", "c")).build().forEach(System.out::println);
```

```
[a, b, c]
```

Streamを生成するbuilder。
`Stream.of`があるので、使う場面少なそう。

# empty
```java
        var empty = Stream.empty();
```

空の`Stream<Object>`を返す。

# of
```java
        Stream.of("a", "b", "c").forEach(System.out::println);
```

```
a
b
c
```

任意の直列streamを返す。

```java
        Stream.of(1, null).forEach(System.out::println);
```

```
1
null
```

nullが混じっていても大丈夫。

```java
        Stream.of(null).forEach(System.out::println);
```

```java
Exception in thread "main" java.lang.NullPointerException: Cannot read the array length because "array" is null
	at java.base/java.util.Arrays.stream(Arrays.java:5428)
	at java.base/java.util.stream.Stream.of(Stream.java:1426)
	at Main.main(Main.java:10)
```

nullだけだとぬるぽで落ちる。

# ofNullable
nullセーフなof。

# iterate
## `static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)`
`seed`は初期値、`f`は次の要素を生成する関数。
※UnaryOperator<T>はざっくり言うと `R apply(T t)`

```java
Stream.iterate(0, n -> n + 1).limit(10).forEach(System.out::println);
```

```
0
1
2
3
4
5
6
7
8
9
```

順序づけされたstreamを生成して返す。
iterateは無限順次ストリームを生成するため、`.limit(10)`などをつけておかないと無限にprintlnで出力されてしまう。

## `static <T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next)`

```java
        Stream.iterate(0, n -> n < 10, n -> n + 1).forEach(System.out::println);
```

```
0
1
2
3
4
5
6
7
8
9
```

`hasNext`にiterateを継続するかどうかの述語を指定することで、stream生成を途中で終了させることができる。

# generate
`static <T> Stream<T> generate(Supplier<? extends T> s)`
```java
        Stream.generate(Math::random).limit(10).forEach(System.out::println);
```

```
0.6367534370722193
0.4060680438123063
0.7259277359859951
0.3210005395437554
0.11408977146578747
0.13146103212325666
0.645913985897788
0.31120448118461697
0.19032175343686475
0.8638467167816094
```

iterateと似ているが、generateは順序づけのない無限順次ストリームを生成して返す。

# concat
```java
        Stream.concat(Stream.of(1, 2, 3), Stream.of(4, 5, 6)).forEach(System.out::println);
```

```
1
2
3
4
5
6
```

2つのstreamを連結する。

```java
        Stream.concat(Stream.of(1, 2, 3).parallel(), Stream.of(4, 5, 6)).forEach(System.out::println);
```

```
4
1
3
2
5
6
```

連結対象の少なくとも一方が並列streamの場合、連結されたstreamも並列になる。