スマートデバイスアプリ開発のあれやこれや

Streamを活用すると綺麗に書けるJavaパターン

はじめに

 ここ数ヶ月くらいJavaのコードレビューをする機会が何回かありました。Javaは社内でも知っている人が多い言語ですが,歴史が長いだけあって昔々の文法を貫いてコード書く人が結構居ます。なかなか新しい要素を勉強し続けるのも辛いと思うのですが,やはり新しいものは良いものが多いので地道に布教活動を続けていきたいと思っています。今回は,特に指摘するケースが多いJava8から使えるようになったStream文に関する内容を整理してみます。

単純なループ

 下のようなコード結構見かけませんか?

List<String> nameList = Arrays.asList("1st", "2nd", "3rd");
for (int i = 0; i < nameList.size(); i++) {
    fetchSomeInformation(nameList.get(i));
}

 いちいちリストの件数を取ってループするのも直感的ではありません。拡張for文を使うのもGoodですが,Streamで置換するのも良い手段だと思います。

List<String> nameList = Arrays.asList("1st", "2nd", "3rd");
nameList.stream().forEach(n -> fetchSomeInformation(n));

ループ中の条件判定

 前の例は単純過ぎたのでもう少し実践的なケースを……。

List<String> nameList = Arrays.asList("Apple", "Orange", "Pine");
nameList.forEach(n -> {
    if (n.length() == 5) {  
        fetchSomeInformation(n);
    }
});

 リストの中身を判定して特定のケースだけで処理を行うコードです。よくやりますよね?こういう処理こそStreamで置換すると効果が大きいと思います。これを置き換えると……。

List<String> nameList = Arrays.asList("Apple", "Orange", "Pine");
    nameList.stream()
        .filter(n -> n.length() == 5)
        .forEach(n -> fetchSomeInformation(n)); 

 こんな感じになります。実行条件と実行内容がそれぞれfilterとforEachに分かれることで処理も見やすくなります。仮に条件が増えた場合も……。

List<String> nameList = Arrays.asList("Apple", "Orange", "Pine");
    nameList.stream()
        .filter(n -> n.length() == 5)
        .filter(n -> n.startsWith("A"))
        .forEach(n -> fetchSomeInformation(n));

 すっきり書けちゃいます。

リストの変換

 リストでループを回しながら,独自のオブジェクトを作る下のようなコード……よくありますよね。

List<String> nameList   = Arrays.asList("Apple", "Orange", "Pine");
List<MyEntity> entities = new ArrayList<>();
for(String name : nameList) {
    entities.add(new MyEntity(name));
}

 こういうケースではmapメソッドが大活躍です。mapはStreamを別の形式に変換する機能を提供しています。

List<String> nameList   = Arrays.asList("Apple", "Orange", "Pine");
List<MyEntity> entieies = nameList.stream()
                            .map(n -> new MyEntity(n))
                            .collect(Collectors.toList());

 かなりスッキリしたコードになりますね!