Java Stream APIについて、まとめてみました。

## はじめに
Java 8から導入されたStream APIは、コレクションや配列などのデータの処理をより簡潔かつ関数的に記述するためのAPIです。従来のfor文を使ったループ処理と比較して、より直感的で、かつ並列処理にも対応できるという特徴があります。コレクションや配列などのデータソースに対して、宣言的な方法でデータ処理を行うことができます。ストリームAPIを使用すると、データのフィルタリング、マッピング、集計などの操作を簡潔に記述できます。
今回は、この「Java Stream APIについて」まとめてみました。

### Stream APIの主な特徴

**宣言型** 何をしたいかを記述するだけで、具体的な処理の仕方はコンパイラが決定。
**遅延評価** 終端操作が実行されるまで、実際には処理が実行されない。
**並列処理** 並列ストリームを利用することで、マルチコアプロセッサの性能を最大限に引き出すことができる。
**関数型** ラムダ式やメソッド参照を用いて、処理を関数として表現する。


### Stream APIのパイプライン

Stream APIは、中間操作と終端操作を組み合わせてパイプラインを構築。

**中間操作** ストリームを変換する操作。filter、mapなどが代表的。
**終端操作** ストリームの処理を終了し、結果を返す操作。forEach、collectなどが代表的。

### Stream APIのメリット

**コードの簡潔化** for文を使ったループ処理と比較して、より少ないコードで記述できる。
**可読性の向上** 処理の流れが視覚的にわかりやすくなる。
**並列処理** 並列ストリームを利用することで、パフォーマンスを向上させることができる。
**関数型プログラミングとの親和性** ラムダ式やメソッド参照を活用することで、関数型プログラミングのスタイルでコードを書くことができる。


### Stream APIの利用例

```java
import java.util.Arrays;
import java.util.List;

public class StreamExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // 偶数だけを抽出
        numbers.stream()
               .filter(n -> n % 2 == 0)
               .forEach(System.out::println);

        // 全ての要素を2倍にする
        List<Integer> doubledNumbers = numbers.stream()
                                              .map(n -> n * 2)
                                              .collect(Collectors.toList());

        // 合計値を求める
        int sum = numbers.stream()
                        .reduce(0, Integer::sum);
    }
}
```

**filter** 条件に合致する要素のみを抽出。
**map** 各要素に対して変換を行う。
**reduce** 全ての要素を1つの値に集約する。
**collect** 結果をコレクションに格納。


### ストリームの作成

ストリームは、コレクションや配列などのデータソースから作成できる。

```java
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class StreamExample {
    public static void main(String[] args) {
        // リストからストリームを作成
        List<String> list = Arrays.asList("apple", "banana", "cherry");
        Stream<String> streamFromList = list.stream();

        // 配列からストリームを作成
        String[] array = {"apple", "banana", "cherry"};
        Stream<String> streamFromArray = Arrays.stream(array);

        // 値からストリームを作成
        Stream<String> streamOfValues = Stream.of("apple", "banana", "cherry");
    }
}
```

### 中間操作

中間操作は、ストリームを変換する操作で、遅延評価されます。中間操作は、ストリームを返すため、連鎖的に呼び出すことができる。

`filter`条件に合致する要素だけを抽出
`map`各要素を変換
`flatMap`ネストされた構造を平坦化する
`sorted`要素をソート

#### フィルタリング

`filter`メソッドを使用して、条件に一致する要素を選択。

```java
List<String> list = Arrays.asList("apple", "banana", "cherry");
list.stream()
    .filter(s -> s.startsWith("a"))
    .forEach(System.out::println); // apple
```

#### マッピング

`map`メソッドを使用して、要素を別の形式に変換。

```java
List<String> list = Arrays.asList("apple", "banana", "cherry");
list.stream()
    .map(String::toUpperCase)
    .forEach(System.out::println); // APPLE, BANANA, CHERRY
```

#### ソート

`sorted`メソッドを使用して、要素をソート。

```java
List<String> list = Arrays.asList("banana", "apple", "cherry");
list.stream()
    .sorted()
    .forEach(System.out::println); // apple, banana, cherry
```

### 終端操作

終端操作は、ストリームの処理を実行し、結果を生成。終端操作は、ストリームを消費するため、再利用できない。

 `collect`結果をコレクションに収集
 `forEach`各要素に対して処理を実行
`reduce`要素を1つに集約
 `anyMatch`、`allMatch`、`noneMatch`要素が条件を満たすかどうかを判定



#### 集約

`collect`メソッドを使用して、ストリームの要素をコレクションに集約。

```java
import java.util.List;
import java.util.stream.Collectors;

List<String> list = Arrays.asList("apple", "banana", "cherry");
List<String> filteredList = list.stream()
    .filter(s -> s.startsWith("a"))
    .collect(Collectors.toList());

System.out.println(filteredList); // [apple]
```

#### カウント

`count`メソッドを使用して、ストリームの要素数をカウント。

```java
List<String> list = Arrays.asList("apple", "banana", "cherry");
long count = list.stream()
    .filter(s -> s.startsWith("a"))
    .count();

System.out.println(count); // 1
```

#### マッチング

`anyMatch`, `allMatch`, `noneMatch`メソッドを使用して、ストリームの要素が条件に一致するかどうかを判定。

```java
List<String> list = Arrays.asList("apple", "banana", "cherry");
boolean anyMatch = list.stream()
    .anyMatch(s -> s.startsWith("a"));

System.out.println(anyMatch); // true
```

#### リダクション

`reduce`メソッドを使用して、ストリームの要素を1つの値に集約。

```java
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
int sum = list.stream()
    .reduce(0, Integer::sum);

System.out.println(sum); // 15
```

### 並列ストリーム

ストリームを並列に処理することで、パフォーマンスを向上させることができる。`parallelStream`メソッドを使用して並列ストリームを作成。

```java
List<String> list = Arrays.asList("apple", "banana", "cherry");
list.parallelStream()
    .map(String::toUpperCase)
    .forEach(System.out::println);
```

### まとめ

JavaのストリームAPIは、コレクションや配列などのデータソースに対して、宣言的な方法でデータ処理を行うためのツール。中間操作と終端操作を組み合わせることで、データのフィルタリング、マッピング、ソート、集約などの操作を簡潔に記述できる。

ストリームAPIを活用することで、コードの可読性と保守性を向上させることができる。

参考文献
徹底攻略Java SE 11 Gold問題集［1Z0-816］対応 
第四章　ストリームAPI
https://book.impress.co.jp/books/1121101020