JavaGold Stream API について理解を深める

# Stream APIとは
JavaのStream APIは、コレクション（リスト、セット、マップなど）のデータ操作を簡潔かつ効率的に行うための機能です。Java 8で導入され、``データ処理をより宣言的（何をするかに焦点を当てる）``に記述できるようにします。これにより、コレクション操作のためのコードが短くなり、読みやすくなります。

![napkin-selection (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3690843/a153eaa8-c96d-1ab5-28a1-79c91b72e9bb.png)


# Stream APIの基本概念
`Stream:` データのシーケンスを表し、要素に対してさまざまな操作（フィルタリング、変換、集約など）を行うためのインターフェース。
`中間操作:` ストリームの要素を変換する操作。結果として新しいストリームを返します（例: map, filter, sorted）。
`終端操作: `ストリームの処理を終了し、結果を返す操作。ストリームを消費して最終結果を返します（例: collect, forEach, reduce）。
### Stream APIの主な特徴
データの操作を宣言的に記述: Stream APIを使用することで、forループを使うことなくデータ操作を記述できます。
`遅延評価:` ストリームの中間操作は遅延評価されます。つまり、終端操作が呼び出されるまで中間操作は実行されません。
`無変更性:` ストリームは元のデータソースを変更せず、新しいストリームを生成します。




# 1. filter
条件に合う要素を選択します。
 ```java
 
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> evenNumbers = numbers.stream()
    .filter(n -> n % 2 == 0) // 偶数のみを選択
    .collect(Collectors.toList());

//例: リストから偶数だけを選択する。
```
# 2. map
各要素を別の形式に変換します。
 ```java
 
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<Integer> nameLengths = names.stream()
    .map(String::length) // 各名前の長さに変換
    .collect(Collectors.toList());

//例: 名前のリストをそれぞれの名前の長さに変換する。
```
# 3. collect
Streamの結果をリストやセット、マップなどに収集します。
 ```java
 
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<String> nameList = names.stream()
    .collect(Collectors.toList());

//例: Streamの結果をリストに収集する。
```
# 4. forEach
各要素に対してアクションを実行します。
 ```java
 
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.stream()
    .forEach(System.out::println); // 各名前を表示

//例: リストの各要素をコンソールに出力する。
```
# 5. reduce
要素を結合して単一の結果を生成します。
 ```java
 
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
    .reduce(0, (a, b) -> a + b); // 合計を計算

//例: リストのすべての要素の合計を計算する。
```
# 6. sorted
要素をソートします。
 ```java
 
List<Integer> numbers = Arrays.asList(5, 3, 1, 4, 2);
List<Integer> sortedNumbers = numbers.stream()
    .sorted() // 昇順にソート
    .collect(Collectors.toList());

//例: リストの要素を昇順にソートする。
```
# 7. distinct
重複する要素を削除します。
 ```java
 
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4, 5);
List<Integer> distinctNumbers = numbers.stream()
    .distinct() // 重複を削除
    .collect(Collectors.toList());

//例: リストから重複する要素を削除する。
```
# 8. limit
最初のN個の要素を取得します。
 ```java
 
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> limitedNumbers = numbers.stream()
    .limit(3) // 最初の3つの要素を取得
    .collect(Collectors.toList());

//例: リストの最初の3つの要素を取得する。
```
# 9. skip
最初のN個の要素をスキップします。
 ```java
 
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> skippedNumbers = numbers.stream()
    .skip(2) // 最初の2つの要素をスキップ
    .collect(Collectors.toList());

//例: リストの最初の2つの要素をスキップする。
```
# 10. findFirst
最初の要素を返します。
 ```java
 
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Optional<String> first = names.stream()
    .findFirst(); // 最初の要素を取得

//例: リストの最初の要素を取得する。
```



# 例: 会社と従業員のデータを処理するstreamAPI
この例では、会社 (Company) と従業員 (Employee) のデータを持っています。各会社は複数の従業員を持ち、各従業員には部門が割り当てられています。このデータを処理して、部門ごとの従業員のリストを取得する処理を実装します。

```java
class Employee {
    private String name;
    private String department;

    public Employee(String name, String department) {
        this.name = name;
        this.department = department;
    }

    public String getName() {
        return name;
    }

    public String getDepartment() {
        return department;
    }

    @Override
    public String toString() {
        return name + " (" + department + ")";
    }
}

class Company {
    private String name;
    private List<Employee> employees;

    public Company(String name, List<Employee> employees) {
        this.name = name;
        this.employees = employees;
    }

    public List<Employee> getEmployees() {
        return employees;
    }

    public String getName() {
        return name;
    }
}

public class AdvancedStreamExample {
    public static void main(String[] args) {
        List<Company> companies = Arrays.asList(
            new Company("CompanyA", Arrays.asList(
                new Employee("Alice", "HR"),
                new Employee("Bob", "Engineering"),
                new Employee("Charlie", "Engineering")
            )),
            new Company("CompanyB", Arrays.asList(
                new Employee("David", "Marketing"),
                new Employee("Eve", "HR"),
                new Employee("Frank", "Engineering")
            ))
        );

        // Stream APIを使用して、部門ごとに従業員をグループ化
        Map<String, List<Employee>> employeesByDepartment = companies.stream()
            .flatMap(company -> company.getEmployees().stream()) // 各会社の従業員を一つのStreamに平坦化
            .collect(Collectors.groupingBy(Employee::getDepartment)); // 部門ごとにグループ化

        // 結果の出力
        employeesByDepartment.forEach((department, employees) -> {
            System.out.println(department + ": " + employees);
        });
    }
}
```

出力結果
このコードの出力は以下のようになります:

```makefile
HR: [Alice (HR), Eve (HR)]
Engineering: [Bob (Engineering), Charlie (Engineering), Frank (Engineering)]
Marketing: [David (Marketing)]
```
# 【参考】
https://docs.oracle.com/javase/jp/8/docs/api/java/util/stream/Stream.html