Java StreamAPIの頻出メソッドメモ

## よく使うstream APIのメソッド

例として、以下のDTOクラスがあるとします。
```java
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;

@Setter
@Getter
@Builder
public class SampleDto {

    private String fieldA;
    private boolean fieldB;
    private int fieldC;
}
```

処理クラスはこんな感じ。
```java
public class Tekito {

    List<SampleDto> dtoList;

    public Tekito(){
        dtoList = new ArrayList<>();
        dtoList.add(SampleDto.builder().fieldA("A1").fieldB(true).fieldC(2).build());
        dtoList.add(SampleDto.builder().fieldA("A2").fieldB(true).fieldC(3).build());
        dtoList.add(SampleDto.builder().fieldA("A3").fieldB(false).fieldC(5).build());
        dtoList.add(SampleDto.builder().fieldA(null).fieldB(true).fieldC(4).build());
        dtoList.add(SampleDto.builder().fieldA("A3").fieldB(false).fieldC(1).build());
    }

    // メソッド
}
```
### フィールドBがすべてtrueであるかのチェック
allMatch()は、与えられた条件にストリームのすべての要素が一致する場合にtrueを返します。
```java
boolean allTrue = dtoList.stream().allMatch(dto -> true == dto.isFieldB());
```

### フィールドBがtrueである要素の個数を取得
```java
long num = dtoList.stream().filter(dto -> true == dto.isFieldB()).count();
```

### フィールドAに3を含む要素が存在するかのチェック

anyMatch()は、与えられた条件にストリームの要素が1つでも一致した場合にtrueを返します。ちなみにallMatchと間違えて使ってしまうことが（私は）多いです。
```java
boolean hasB3 = dtoList.stream().anyMatch(dto -> dto.getFieldA().contains("3"));
```

### フィールドAの値がA4である要素がひとつもなければtrueを取得

noneMatch()は、与えられた条件にストリームの要素が1つも一致しなかった場合にtrueを返します。
```java
boolean falseExistsFlg = dtoList.stream().noneMatch(dto -> "A4".equals(dto.getFieldA()));
```
### フィールドCが最も大きい要素を取得
```java
Optional<SampleDto> maxCElement = dtoList.stream().max(Comparator.comparingInt(SampleDto::getFieldC));

if (maxCElement.isPresent()) {
    SampleDto dto = maxCElement.get();
    System.out.println("FieldA: " + dto.getFieldA() + ", FieldB: " + dto.isFieldB() + ", FieldC: " + dto.getFieldC());
} else {
    System.out.println("リストが空です。");
}
```

### フィールドBの値がfalseである最初の要素を取得
```java
Optional<SampleDto> firstFalseB = dtoList.stream().filter(dto -> false == dto.isFieldB()).findFirst();

if (firstFalseB.isPresent()) {
    SampleDto dto = firstFalseB.get();
    System.out.println("FieldA: " + dto.getFieldA() + ", FieldB: " + dto.isFieldB() + ", FieldC: " + dto.getFieldC());
}else{
    System.out.println("フィールドBがfalseである要素はありません。");
}
```

### フィールドCの値の降順にソート
```java
var sortedList = dtoList.stream()
                        .sorted(Comparator.comparing(SampleDto::getFieldC).reversed())
                        .collect(Collectors.toList());
sortedList.forEach(dto -> System.out.println(dto.getFieldA() + ", " + dto.isFieldB() + ", " + dto.getFieldC()));
```

### フィールドAの値が、他の要素のフィールドAにも存在するかどうかを判定

```java
Map<String, Long> fieldACountMap = dtoList.stream()
    .map(SampleDto::getFieldA)
    .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
boolean hasDuplicate = fieldACountMap.values().stream().anyMatch(count -> count > 1);
```
フィールドAの値ごとの出現数をMap<String, Long>型変数に格納します。
その上で、anyMatchで判定します。
Stream APIはListに対して使うイメージが強いですが、Mapについても使うことができます。


## 補足：ラムダ式内のreturnはメソッドを終了するものではない

下記のコードがあるとする。
リストの要素にnullがあれば処理を終了する。
実行すると、「終わり」は出力されない。

```java

    public static void main(String[] args) {
        
        List<String> list = new ArrayList<>();
        list.add("111");
        list.add("222");
        list.add("333");
        list.add(null);
        list.add("444");

        for (String element : list){
            if (element == null){
                return;
            }
        }

        System.out.println("終わり");
    }

```

これをラムダ式に変えるとこうなる。

```java

    public static void main(String[] args) {
        
        List<String> list = new ArrayList<>();
        list.add("111");
        list.add("222");
        list.add("333");
        list.add(null);
        list.add("444");

        // ラムダ式を使用してnull要素がある場合にreturnする
        list.forEach(element -> {
            if (element == null) {
                return;
            }
        });

        System.out.println("終わり");
    }
```

しかし、ラムダ式とは無名クラスをさらに簡略化した書き方（厳密には違う模様ではありますが）なので、ラムダ式内のreturnは、そのラムダ式の実行を終了させるだけで、mainメソッドの後続のコードは実行されます。つまり、「終わり」は出力されます。