Java8以降 Optional streamの使い方

Java
スポンサーリンク

Java8以降に登場したOptionalとstreamの使い方についてです。使い方を知っていれば、とても便利で今まで数行かけて記述していたプログラムが一行で書けてしまいます。

しかし、一行で書けるのはよいのですが、初見の人にはやや分かり難いのかなという印象です。

ここでは、Optionalとstreamの便利な使い方について、簡単ですが説明したいと思います。

Optional、streamの使い方

まず、Optionalとstreamは一緒に使うことが多いです。そうでない場合もありますが、、、わたしはよく合わせて使っています。

OptionalのOracleでのクラス説明は以下の通りです。

null以外の値が含まれている場合も含まれていない場合もあるコンテナ・オブジェクトです。

streamは、以下の通りとなっています。

順次および並列の集約操作をサポートする要素のシーケンスです。

説明だけ読むと?となりますので、サンプルを作成してみました。

1つ目がMainクラス、2つ目がSchoolクラスです。ここでは、引数で学校コードを受け取り、それに一致する学校を取得する簡単なサンプルです。(入力チェックは簡易的になっていますので、ご了承ください。)

import dto.School;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

public class ApplicationMain {

    public static void main(String args[]){
        
        // 準備
        List<School> schoolList = new ArrayList<>();
        School school = new School();
        school.setSchoolCode("001");
        school.setSchoolName("学校1");
        schoolList.add(school);

        school = new School();
        school.setSchoolCode("002");
        school.setSchoolName("学校2");
        schoolList.add(school);

        school = new School();
        school.setSchoolCode("003");
        school.setSchoolName("学校3");
        schoolList.add(school);

        // 引数で与えられた学校コードに一致する学校を取得する★Point!★
        Optional<School> target = schoolList.stream().filter(f->f.getSchoolCode().equals(args[0])).findFirst();

        if (target.isPresent()) {
            School school1 = target.get();
            System.out.println(school1.getSchoolCode());
            System.out.println(school1.getSchoolName());
        } else {
            System.out.println("存在しない学校コードです");
        }
    }
}
package dto;

import lombok.Getter;
import lombok.Setter;

@Setter
@Getter
public class School {

    private String schoolCode;

    private String schoolName;

}

上記サンプルでは、引数arg[0]は、必ずしもリストに存在する値でないという点がポイントです。streamでfilterし、学校を取得します。しかし、それが必ずしも存在するとは限らない場合に一度Optionalで受けることが可能です。

値が存在する場合は、「isPresent()」がtrueになります。nullであることを確認する場合は、「isEmpty()」を使用します。

Optionalとstreamを使用しない場合は、以下のように記載することができます。(プログラムの書き方は色々ありますので、一つの例です。)以下のfor文をstreamを使うことで、1行で記載することが可能です。

// 引数で与えられた学校コードに一致する学校を取得する
School target = new School();
for(School school1 : schoolList) {
    if (school1.getSchoolCode().equals(args[0])) {
        target = school1;
    }
}
System.out.println(target.getSchoolCode());
System.out.println(target.getSchoolName());

 

もう少し、発展系をみてみましょう。streamの「findFirst()」「findAny()」はOptional型を返します。Optional型では、値がnullだった場合の動作を記述することができます。

以下の例では「orElse(null)」としていますので、値が取得できなかった場合は、Schoolにnullを設定して、返すことになります。

School target = schoolList.stream().filter(f->f.getSchoolCode().equals(args[0])).findFirst().orElse(null);

 

以下のように記述すると例外をスローすることができます。

School target = schoolList.stream().filter(f->f.getSchoolCode().equals(args[0])).findFirst().orElseThrow(IllegalArgumentException::new);

 

また、以下のように値が存在する場合だけ処理を実行することも可能です。ifPresentを使用することで、あとからif文でisPresent()を使用する必要もなくなりますね。

schoolList.stream().filter(f->f.getSchoolCode().equals(args[0])).findFirst().ifPresent(school1 -> {
    System.out.println(school1.getSchoolCode());
    System.out.println(school1.getSchoolName());
});

まとめ

Optionalやstreamを使用するメリットを少しは感じていただけたでしょうか?特にstreamは、上に挙げた以外にも色々な使い方があり、とても便利です。必要に応じて使えるようになると技術者としてのレベルが上がるのではないでしょうか。

プロジェクトによっては、分かり難いので使用しないというところもあるかもしれませんが、、、すでにこちらの記述が主流になっているプロジェクトも多いのではないでしょうか。参考までに。

コメント