データ構造の処理のパターンとStream

データ構造の処理のパターンとして「プロになるJava」の「10章データ構造の処理」「値の集合の処理のパターン」では新しいListを作るものと個数を数えるもののふたつを紹介していますが、追加でもうふたつ。

データ構造の処理の例

条件に合う要素全てが別の条件にあうか判定

5文字以上の文字列がすべてyを含むか判定するコードは次のようになります。

package projava;

import java.util.List;

public class StreamSample3 {
    public static void main(String[] args) {
        var data = List.of("yamamoto", "kis", "sugiyama");
        
        var result = true;
        for (var s : data) {
            if (s.length() >= 5) {
                result &= s.contains("y");
            }
        }
        System.out.println(result);
    }
}

ここで&=は変数の値と&演算、つまりandを行ったものを、元の変数に割り当てなおすという複合代入演算子result = result & s.contains("y")と同じ動きになります。

条件に合う要素のひとつでも別の条件にあうか判定

5文字以上の文字列がひとつでもgを含むか判定するコードは次のようになります。

package projava;

import java.util.List;

public class StreamSample4 {
    public static void main(String[] args) {
        var data = List.of("yamamoto", "kis", "sugiyama");
        
        var result = false;
        for (var s : data) {
            if (s.length() >= 5) {
                result |= s.contains("g");
            }
        }
        System.out.println(result);
    }
}

ここで|=は変数の値と|演算、つまりorを行ったものを、元の変数に割り当てなおすという複合代入演算子result = result | s.contains("g")と同じ動きになります。

共通の構造

新しいListを作る、個数を数える、すべてがyを含むか判定、どれかがgを含むか判定のサンプルを見ましたが、これらには共通の構造があります。

var result = 初期値;
for (var s : data) {
  if (s.length() >= 5) {
    resultに新たな結果を加える処理
  }
}

この中で、初期値や新たな結果を加える処理は次のようになっています。

処理 初期値 結果を加える処理
新しいList new ArrayList() result.add(s)
個数を数える 0 result++
全部yを含むか true result &= s.contains("y")
gを含むものがあるか false result |= s.contains("g")

Stream

プログラムにおいて「パターンがあるのであれば共通化して覚えなくてもよくしよう」というのが大切ですが、データ構造の処理を共通化するのがStreamといえます。

今回の処理はそれぞれ、allMatchとanyMatchを終端処理に使うことで書き換えることができます。

var result = data.stream()
             .filter(s -> (s.length() >= 5))
             .allMatch(s -> s.contains("y"));
var result = data.stream()
             .filter(s -> (s.length() >= 5))
             .anyMatch(s -> s.contains("g"));

これらをまとめると、次のようになります。

処理 終端処理
新しいList toList()
個数を数える count()
全部yを含むか allMatch(s -> s.contains("y"))
gを含むものがあるか anyMatch(s -> s.contains("g"))