プロジェクト

全般

プロフィール

Javaプログラミング ラムダ式とStream API

Java SE 8で導入されたラムダ式と、ラムダ式を使用するコレクションAPI(Stream API)を使うとかなり高レベルなデータ処理ができるようになりますが、そのプログラミングはパラダイムシフトを少し必要とします。慣れるのに練習が必要と思われます。

メモ集

Lamda道場

Streamの生成

コレクション(Mapを除く)からStreamの生成

Javaのコレクションフレームワークでは、基底となるインタフェースCollection型にStreamを返すstreamメソッドが用意されています。

コレクション(Map)からStreamの生成

MapはCollectionを継承していないので、streamメソッドがありません。というより、Mapはストリーム(列)をなしていないので、entrySetを取り出してstreamを生成するのかと思います。

Map countries = ...
countries.entrySet().stream()
                    .map(e -> e.getValue().getName())
                    .forEach(System.out::println);

配列をStreamにする

ArraysからStreamの生成
Arrays.stream(args)

でStreamを生成できます。
Streamのofで生成

Streamクラスのstaticメソッド of は、可変長引数で受けた引数のStreamを生成します。可変長引数は配列として扱われるので、ofメソッドに配列を渡すことでStreamを生成することができます。

Point[] points = ...
Stream.of(points).forEach(...);

また、プリミティブ型に対応するIntStream、LongStream、DoubleStreamのそれぞれのofメソッドで、プリミティブ型の配列からStreamを生成します。

整数のStream生成

IntStream.range(1, 10)

で、1から9までの整数の順列のStreamを生成できます。
new Random().ints(-5, 5)

で、-5以上5未満の整数の無限Streamを生成します。

Streamの並列化(Parallel)

Streamに対してparallel()を呼ぶことで、処理を並列化できます。

オーバーヘッドとトレードオフ

Streamを並列化するには初期オーバーヘッドが発生します。おおよそ100μ秒といわれています。

  • 処理時間が100μ秒以下なら並列化すべきではない
  • N × Q >= 10,000なら並列化を考慮する
    • Nは要素数
    • Qは要素あたりの処理コスト(整数加算を1とした)
      • 3次元デカルト座標系の距離計算=7、3次元デカルト座標系の大きさ計算(平方根処理なしの距離計算)=3.3
  • Streamのソースは分割可能で要素の処理は独立している

コード

ラムダ式

Stream APIを使わないラムダ式によるコード例は次のページに独立。

検索

UNIXコマンドのgrep的な処理です。

anyMatch

com.sun.javadoc.Tagクラスの配列から、text()で取り出した文字列が"1.8"、"8"、または"JDK1.8"のいずれかの文字列と一致したらtrueを、いずれとも一致しなければfalseを返すメソッドを記述します。

従来の書き方 Stream APIを使った書き方
private static boolean isSinceTagMatches(Tag[] tags) {
    for (Tag tag : tags) {
        if ("1.8".equals(tag.text())) {
            return true;
        } else if ("8".equals(tag.text())) {
            return true;
        } else if ("JDK1.8".equals(tag.text())) {
            return true;
        }
    }
    return false;
}
private static boolean isSinceTagMatches(Tag[] tags) {
    return Arrays.stream(tags)
        .map(tag -> tag.text())
        .anyMatch(text -> "1.8".equals(text) || "8".equals(text) || "JDK1.8".equals(text));
}

distinct

1件1行で出力されたログファイルがあり、各行は先頭にリクエスト元のクライアントのIPアドレスと、続いて各要素が空白区切りで出力されています。
このファイルを読み、IPアドレス項を取得し、重複をなくして標準出力に出力します。

従来の書き方 Stream APIを使った書き方
BufferedReader reader = null;
try {
    reader = new BufferedReader(
        new InputStreamReader(
            new FileInputStream(new File(fileName)), "UTF-8"));
    String line;
    Set<String> ipAddresses = new HashSet<>();
    while ((line = reader.readLine()) != null) {
        String[] words = line.split(" ");
        ipAddresses.add(words[0]);
    }
    for (String ipAddress : ipAddresses) {
        System.out.println(ipAddress);
    }
} catch (IOException ex) {
    // ファイル読み込み時のエラー処理
} finally {
    if (reader != null) {
        try {
            reader.close();
        } catch (IOException ex) {
            // ignored
        }
    }
}
try (BufferedReader reader = Files.newBufferedReader(
    Paths.get(fileName), StandardCharsets.UTF_8)) {
    reader.lines()
          .map(s -> s.split(" ")[0])
          .distinct()
          .forEach(System.out::println);
} catch (IOException ex) {
    // ファイル読み込み時のエラー処理
}

allMatch

素数を判定するコード

従来の書き方 Stream APIを使った書き方
boolean isPrime(int number) {
    for (int i = 2; i < number; i++) {
        if (number % i == 0) {
            return false;
        }
    }
    return true;
}
boolean isPrime(int number) {
    return IntStream.range(2, number)
                    .allMatch(x -> (number % x) != 0);
}

リンク集

オンライン記事

ITPro連載:Java技術最前線

セミナー資料

ブログから

クリップボードから画像を追加 (サイズの上限: 1 GB)