倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

小川のせせらぎもやがてはうねる奔流に Stream インターフェース (3) - short-circuiting terminal operators -

Java の Stream インターフェースに定義されているメソッドを見ていくシリーズ(目次)。 今回は Stream インターフェースに定義されている short-circuiting terminal operator に分類されるメソッドを見ていきます。 terminal operator は前回まで扱っていた「終端演算子」ですが「short-circuiting」は Java の && 演算子のような「ショートカット(演算子)」であることを表しています。 つまり、必要な結果が得られればそれ以降の要素に対する処理をやめて制御を返すような終端演算子です。 扱うメソッドは次の5つ:

// Match
boolean allMatch(Predicate<? super T> predicate)
boolean anyMatch(Predicate<? super T> predicate)
boolean noneMatch(Predicate<? super T> predicate)

// Find
Optional<T> findFirst()
Optional<T> findAny()

では Match と Find に分けて見ていきましょう。

Match

Match は Predicate オブジェクト(述語)*1を引数にとり、Stream の各要素に対してその述語に対応するテストを行い、その結果をまとめるメソッドです:

メソッド true を返す場合 空 Stream に対して 備考
allMatch() 全ての要素に対して結果が true true を返す 各要素の結果値に対する論理積
anyMatch() 少なくとも1つの要素に対して結果が true false を返す 各要素の結果値に対する論理和
noneMatch() 全ての要素に対して結果が false true を返す anyMatch() の否定

返り値が false になるのは true を返す場合以外です。 空 Stream に対してもそれぞれ定められた boolean 値が返されます。 ではサンプルコード:

// Stream オブジェクトの生成メソッド
public static Stream<String> stream(){
    return Stream.of("Java", "Groovy", "Scala", "Clojure", "Kotlin", "Jython", "JRuby");
}

// Predicate オブジェクト(述語)
Predicate<String> starts_with_upper_case = s -> Character.isUpperCase(s.charAt(0));
Predicate<String> starts_with_J          = s -> s.startsWith("J");
Predicate<String> ends_with_J            = s -> s.endsWith("J");

// allMatch()
assert  stream().allMatch(starts_with_upper_case);
assert !stream().allMatch(starts_with_J);
assert !stream().allMatch(ends_with_J);

assert Stream.<Boolean>empty().allMatch(arg -> true);    // 常に true
assert Stream.<Boolean>empty().allMatch(arg -> false);

// anyMatch()
assert  stream().anyMatch(starts_with_upper_case);
assert  stream().anyMatch(starts_with_J);
assert !stream().anyMatch(ends_with_J);

assert !Stream.<Boolean>empty().anyMatch(arg -> true);    // 常に false
assert !Stream.<Boolean>empty().anyMatch(arg -> false);

// noneMatch()
assert !stream().noneMatch(starts_with_upper_case);
assert !stream().noneMatch(starts_with_J);
assert  stream().noneMatch(ends_with_J);

assert Stream.<Boolean>empty().noneMatch(arg -> true);    // 常に true
assert Stream.<Boolean>empty().noneMatch(arg -> false);

まぁ、結構分かりやすいメソッドではないでしょうか。 空 Stream に対する返り値がスッと頭に入ってくるかどうかってところかな。

Find

Find は要素を探索するメソッドです。 こちらは引数に何もとらず、それぞれ適切な要素の Optional を返します。 空 Stream に対しては空 Optional が返されます。

  • findFirst() ・・・ 出現順序に従って最初に位置する要素を返す
  • findAny() ・・・ Stream 内のどれか1つの要素を返す

これらのメソッドは通常それ単体では使わず、次回にやる filter() メソッドなどとともに用いることが多いと思います。 というか、メソッドの引数にフィルターに相当する Predicate オブジェクトをとるバージョンもオーバーロードしてくれといてくれたらいいと思うのだが・・・ まぁ filter() メソッド使うのと変わらんが。 サンプルコードいってみよう:

// Stream オブジェクトの生成メソッド
public static Stream<String> stream(){
    return Stream.of("Java", "Groovy", "Scala", "Clojure", "Kotlin", "Jython", "JRuby");
}

// テスト用の either() メソッド target が args の内のどれかと等しいなら true を、そうでなければ false を返します
public static boolean either(String target, String... args){
    for(String s : args){
        if(target.equals(s))return true;
    }
    return false;
}

// Predicate オブジェクト(述語)
Predicate<String> starts_with_J = s -> s.startsWith("J");
Predicate<String> ends_with_J  = s -> s.endsWith("J");

// findFirst()
assert stream().filter(starts_with_J).findFirst() .equals( Optional.of("Java") );
assert stream().filter(ends_with_J).findAny() .equals( Optional.empty() );
    	
// findAny()
String str = stream().filter(starts_with_J).findAny() .get();
assert either(str, "Java", "Jython", "JRuby");
assert stream().filter(ends_with_J).findAny() .equals( Optional.empty() );

結構使い勝手は良さそう。

次回は intermediate operators part 1 の予定。

Scalaスケーラブルプログラミング第2版

Scalaスケーラブルプログラミング第2版

*1:boolean 値を返す test() メソッドが定義されている。