よく使うstream APIのメソッド
例として、以下のDTOクラスがあるとします。
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
@Builder
public class SampleDto {
private String fieldA;
private boolean fieldB;
private int fieldC;
}
処理クラスはこんな感じ。
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を返します。
boolean allTrue = dtoList.stream().allMatch(dto -> true == dto.isFieldB());
フィールドBがtrueである要素の個数を取得
long num = dtoList.stream().filter(dto -> true == dto.isFieldB()).count();
フィールドAに3を含む要素が存在するかのチェック
anyMatch()は、与えられた条件にストリームの要素が1つでも一致した場合にtrueを返します。ちなみにallMatchと間違えて使ってしまうことが(私は)多いです。
boolean hasB3 = dtoList.stream().anyMatch(dto -> dto.getFieldA().contains("3"));
フィールドAの値がA4である要素がひとつもなければtrueを取得
noneMatch()は、与えられた条件にストリームの要素が1つも一致しなかった場合にtrueを返します。
boolean falseExistsFlg = dtoList.stream().noneMatch(dto -> "A4".equals(dto.getFieldA()));
フィールドCが最も大きい要素を取得
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である最初の要素を取得
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の値の降順にソート
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にも存在するかどうかを判定
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型変数に格納します。
その上で、anyMatchで判定します。
Stream APIはListに対して使うイメージが強いですが、Mapについても使うことができます。
補足:ラムダ式内のreturnはメソッドを終了するものではない
下記のコードがあるとする。
リストの要素にnullがあれば処理を終了する。
実行すると、「終わり」は出力されない。
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("終わり");
}
これをラムダ式に変えるとこうなる。
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メソッドの後続のコードは実行されます。つまり、「終わり」は出力されます。