Stream API の `filter`、`peek`、`collect` をメソッド化または引数にする方法

### **Stream API の `filter`、`peek`、`collect` をメソッド化または引数にする方法**

Stream API の `filter`、`peek`、`collect(Collectors.toList())` を利用した処理は、メソッド化や関数型インターフェースを用いたパラメータ化が可能です。  

仕事で困っていたのでChatGPTの答えを貼り付け。

以下に、いくつかの方法を紹介します。

---

## **1. ストリーム処理をメソッド化する**
### **(1) Stream を引数にして処理を適用**
Stream の処理を **メソッドとして切り出す** 方法です。

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

public class StreamExample {
    
    // ストリーム処理をメソッド化
    public static List<String> processStream(Stream<String> stream) {
        return stream
                .filter(s -> s.startsWith("A")) // "A" で始まるものをフィルタ
                .peek(s -> System.out.println("Filtered: " + s)) // ログ出力
                .collect(Collectors.toList()); // リスト化
    }

    public static void main(String[] args) {
        List<String> data = List.of("Apple", "Banana", "Avocado", "Orange");

        // メソッドを使う
        List<String> result = processStream(data.stream());

        System.out.println(result); // [Apple, Avocado]
    }
}
```

**ポイント**
- `Stream<String>` を受け取るメソッドを定義し、`filter`・`peek`・`collect` を適用。
- 任意の `Stream` に適用できる汎用的なメソッドとして利用可能。

---

## **2. 関数型インターフェース (`Function`) を使ってメソッドの引数に渡す**
**ストリーム処理を `Function<Stream<T>, List<T>>` にまとめ、メソッドの引数として渡す** 方法です。

```java
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamExample {

    // 汎用的なストリーム処理
    public static <T> List<T> applyStreamFunction(Stream<T> stream, Function<Stream<T>, List<T>> function) {
        return function.apply(stream);
    }

    public static void main(String[] args) {
        List<String> data = List.of("Apple", "Banana", "Avocado", "Orange");

        // 処理を Function として定義
        Function<Stream<String>, List<String>> processStream = s -> s
                .filter(str -> str.startsWith("A"))
                .peek(str -> System.out.println("Filtered: " + str))
                .collect(Collectors.toList());

        // メソッドに関数を渡して適用
        List<String> result = applyStreamFunction(data.stream(), processStream);

        System.out.println(result); // [Apple, Avocado]
    }
}
```

**ポイント**
- `Function<Stream<T>, List<T>>` を使い、ストリーム処理の適用方法を柔軟に変更できる。
- どのような `Stream<T>` にも適用できる汎用メソッド。

---

## **3. `UnaryOperator<Stream<T>>` を使う**
`UnaryOperator<Stream<T>>` を使って、ストリーム自体を操作する方法です。

```java
import java.util.List;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamExample {

    // UnaryOperator を使ってストリーム処理を抽象化
    public static <T> List<T> processWithOperator(List<T> data, UnaryOperator<Stream<T>> operator) {
        return operator.apply(data.stream()).collect(Collectors.toList());
    }

    public static void main(String[] args) {
        List<String> data = List.of("Apple", "Banana", "Avocado", "Orange");

        // ストリーム処理を UnaryOperator として定義
        UnaryOperator<Stream<String>> streamOperator = s -> s
                .filter(str -> str.startsWith("A"))
                .peek(str -> System.out.println("Filtered: " + str));

        // メソッドに処理を渡して適用
        List<String> result = processWithOperator(data, streamOperator);

        System.out.println(result); // [Apple, Avocado]
    }
}
```

**ポイント**
- `UnaryOperator<Stream<T>>` は `Stream<T>` を受け取り、処理を適用した `Stream<T>` を返す。
- `collect(Collectors.toList())` はメソッド内で適用することで、ストリーム操作を統一できる。

---

## **まとめ**
| 方法 | メリット | デメリット |
|------|---------|-----------|
| **1. メソッド化 (`Stream` を引数にする)** | 簡潔で分かりやすい | `Stream<T>` を都度渡す必要がある |
| **2. `Function<Stream<T>, List<T>>` を使う** | 汎用的に使いやすい | `Function` の記述がやや複雑 |
| **3. `UnaryOperator<Stream<T>>` を使う** | ストリーム処理を簡潔にカプセル化 | `collect(Collectors.toList())` を適用する箇所に注意 |

**おすすめ**
- **一番シンプルなのは ① のメソッド化**
- **柔軟性を求めるなら ② の `Function` を引数にする方法**
- **ストリーム自体の操作に重点を置くなら ③ の `UnaryOperator`**

どれを選ぶかは、**「どの程度の汎用性を求めるか」** によります。  
シンプルなケースではメソッド化、より柔軟にするなら `Function` や `UnaryOperator` を使うのが良いでしょう。