のんびりプログラミング生活

画像処理、CG、プログラミングなどの話題を中心にのんびり記事を書きます。不定期更新。

ゆるーくざっくりラムダ式とStream【Java8】

前書き

Java9のリリースは2017年7月らしいですが、未だに入門書では8の記述について全然書いてなかったり。このまま9がリリースされたら知らない機能が山積みに!というわけで、Java8のおさらいとして、ゆるーくラムダ式とStreamについてみていこう。

関数型プログラミングの前に

その前に。よくある従来の書き方を見てみよう。

例えば、リストに入った3人の名前を、順番に表示したいとか。

for文を使えば楽勝だね。

List<String> names = new ArrayList<>();
    names.add("alice");
    names.add("bob");
    names.add("cherie");

    // 1 よくあるやつ
    for (int i = 0; i < names.size(); i++) {
        System.out.println(names.get(i));
    }


ArrayListにアリスさん、ボブさん、シェリーさんを追加して、for文で順番に表示。
入門書でよく見る形のはず。

ぱっと見で全部表示するんだろうなーとわかるんだけど、スマートじゃないので次のように書き替えてみる(ArrayListは省略)。

// 2 拡張for文
for(String name : names)
    System.out.println(name);

拡張for文を使えば簡潔で、分かりやすい。

Java8の機能を使ってみよう

forEach

forEachメソッドを使うとこんな感じ。

// 3 forEachメソッド 内部イテレータ
names.forEach(new Consumer<String>(){
    public void accept(String name){
        System.out.println(name);
    }
});

forEachメソッドは関数型インターフェースのConsumerを引数にする。

ラムダ式

お待ちかねのラムダ式

// 4 ラムダ式
names.forEach(name -> System.out.println(name));

アロー演算子みたいなやつを書く。
めちゃくちゃ簡潔で読みやすい(nameに入れて表示してるんだなって一目でわかる)けど、もっと短くもできる。それがメソッド参照。

// 5 メソッド参照を使ってもっと短く
names.forEach(System.out::println);

Stream

今度はStream()を使ってみよう。
mapという要素を変換する中間操作を行っている。forEachはさっき見た。

// 6 Streamインターフェース
// 名前を全て大文字にして出力したい!
names.stream()
    .map(name -> name.toUpperCase())
    .forEach(name -> System.out.println(name));

    // 7 メソッド参照を使ってもっと短く
    names.stream()
        .map(String::toUpperCase)
        .forEach(System.out::println);

filter

文字の検索なんかを行いたいときにはfilterが使える。

// 8 filter() 検索
// bから始まる名前(bob)を出力したい!
List<String> Bname =
    names.stream()
        .filter(name -> name.startsWith("b"))
        .collect(Collectors.toList());
        
    for (String s : Bname) {
         System.out.println(s);
    }

プログラムと実行結果

上記全てをまとめると以下。

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args){
        List<String> names = new ArrayList<>();
        names.add("alice");
        names.add("bob");
        names.add("cherie");

        // 1 よくあるやつ
        for (int i = 0; i < names.size(); i++) {
            System.out.println(names.get(i));
        }

        // 2 拡張for文
        for(String name : names){
            System.out.println(name);
        }

        // 3 forEachメソッド 内部イテレータ
        names.forEach(new Consumer<String>(){
            public void accept(String name){
                System.out.println(name);
            }
        });

        // 4 ラムダ式
        names.forEach(name -> System.out.println(name));

        // 5 メソッド参照を使ってもっと短く
        names.forEach(System.out::println);

        // 6 Streamインターフェース
        // 名前を全て大文字にして出力したい!
        names.stream()
                .map(name -> name.toUpperCase())
                .forEach(name -> System.out.println(name));

        // 7 メソッド参照を使ってもっと短く
        names.stream()
                .map(String::toUpperCase)
                .forEach(System.out::println);

        // 8 filter() 検索
        // bから始まる名前(bob)を出力したい!
        List<String> Bname =
                names.stream()
                        .filter(name -> name.startsWith("b"))
                        .collect(Collectors.toList());
        
        for (String s : Bname) {
            System.out.println(s);
        }
    }
}

実行結果

alice
bob
cherie
alice
bob
cherie
alice
bob
cherie
alice
bob
cherie
alice
bob
cherie
ALICE
BOB
CHERIE
ALICE
BOB
CHERIE
bob