Java | Java 詳細・モダン文法:Stream API 深掘り – allMatch / anyMatch

Java Java
スポンサーリンク

allMatch / anyMatch を一言でいうと

allMatchanyMatch は、
「Stream の要素が“条件を満たしているかどうか”を、真偽値で答える終端操作」です。

allMatch は「全部そうか?」、
anyMatch は「ひとつでもそうか?」を聞くメソッドです。

どちらも戻り値は boolean で、
filter のように要素を流し続けるのではなく、「判定したら終わり」というタイプの処理になります。


共通点:Predicate を受け取る「条件チェック」の終端操作

シグネチャと役割

どちらも、Predicate<T>T -> boolean な関数)を 1 つ受け取ります。

boolean allMatch(Predicate<? super T> predicate)
boolean anyMatch(Predicate<? super T> predicate)
Java

Predicate<T> は、

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}
Java

という関数型インターフェースで、
「要素 1 つを受け取って true / false を返す“条件”」を表します。

つまり、

allMatch / anyMatch は、
「この条件(Predicate)に対して、Stream 全体として true か false か?」
を答えるメソッドです。


anyMatch:ひとつでも条件を満たせば true

基本イメージとシンプルな例

anyMatch は、
「1 つでも条件を満たす要素があれば true、1 つもなければ false」
を返します。

import java.util.List;

public class AnyMatchBasic {
    public static void main(String[] args) {
        List<Integer> nums = List.of(1, 3, 5, 8, 9);

        boolean hasEven =
                nums.stream()
                    .anyMatch(n -> n % 2 == 0);

        System.out.println(hasEven); // true(8 が偶数だから)
    }
}
Java

ここでは、n -> n % 2 == 0 が「偶数かどうか」の条件です。
8 が条件を満たすので、anyMatch は true を返します。

もしリストが List.of(1, 3, 5, 7, 9) なら、
偶数が 1 つもないので false になります。

「見つかったらすぐ終わる」短絡評価

anyMatch は、
「条件を満たす要素を見つけた時点で、残りは見ずに処理を打ち切る」
という動きをします。

これは「短絡評価」と呼ばれ、

if (a || b)a が true なら b を評価しない

のと同じイメージです。

要素数が多いときでも、
「最初の方で条件を満たすものが見つかれば、残りを見ずに済む」
というのがパフォーマンス上のメリットになります。


allMatch:全部が条件を満たしていれば true

基本イメージとシンプルな例

allMatch は、
「すべての要素が条件を満たしていれば true、1 つでも満たさないものがあれば false」
を返します。

import java.util.List;

public class AllMatchBasic {
    public static void main(String[] args) {
        List<Integer> nums = List.of(2, 4, 6, 8);

        boolean allEven =
                nums.stream()
                    .allMatch(n -> n % 2 == 0);

        System.out.println(allEven); // true(全部偶数)
    }
}
Java

ここでは、全要素が偶数なので true です。

もし List.of(2, 4, 5, 8) のように 1 つでも奇数が混ざっていれば、
allMatch は false を返します。

こちらも「ダメなやつを見つけたらすぐ終わる」

allMatch も短絡評価をします。

「条件を満たさない要素を 1 つでも見つけたら、その時点で false を返し、残りは見ない」

という動きです。

これは、

if (a && b)a が false なら b を評価しない

のと同じイメージです。


実用的な例:User のリストで考える

例1:全員が成人かどうかをチェックする(allMatch)

import java.util.List;

class User {
    private final String name;
    private final int age;
    User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    int getAge() { return age; }
}

public class AllMatchUserExample {
    public static void main(String[] args) {
        List<User> users = List.of(
                new User("Alice", 20),
                new User("Bob", 25),
                new User("Charlie", 19)
        );

        boolean allAdult =
                users.stream()
                     .allMatch(u -> u.getAge() >= 18);

        System.out.println(allAdult); // false(Charlie が 18 未満)
    }
}
Java

ここでは、
「全員が 18 歳以上か?」を allMatch でチェックしています。

1 人でも 18 未満がいれば false なので、
「全員が条件を満たしているか?」という問いにぴったりです。

例2:誰か 1 人でもアクティブユーザーがいるか(anyMatch)

class User {
    private final String name;
    private final boolean active;
    User(String name, boolean active) {
        this.name = name;
        this.active = active;
    }
    boolean isActive() { return active; }
}

public class AnyMatchUserExample {
    public static void main(String[] args) {
        List<User> users = List.of(
                new User("Alice", false),
                new User("Bob", false),
                new User("Charlie", true)
        );

        boolean hasActive =
                users.stream()
                     .anyMatch(User::isActive);

        System.out.println(hasActive); // true(Charlie が active)
    }
}
Java

ここでは、
「アクティブなユーザーが 1 人でもいるか?」を anyMatch でチェックしています。

「1 人でもいれば OK」という問いには、anyMatch が自然にハマります。


filter + findAny と anyMatch の違い

どちらも「1 件でもあるか?」を調べられる

実は、

stream.anyMatch(cond)
Java

stream.filter(cond).findAny().isPresent()
Java

は、意味としてはほぼ同じです。

ただし、設計としては次のように使い分けるとスッキリします。

「要素そのものが欲しい」なら filter + findAny / findFirst
「要素があるかどうかだけ知りたい」なら anyMatch

例えば、

boolean hasAdmin =
        users.stream()
             .anyMatch(User::isAdmin);
Java

の方が、

boolean hasAdmin =
        users.stream()
             .filter(User::isAdmin)
             .findAny()
             .isPresent();
Java

よりも、「何をしたいコードか」が一目で分かります。


allMatch / anyMatch と「空の Stream」

空のときの挙動はどうなるか

ここは少し引っかかりやすいポイントなので、意識しておくと安心です。

空の Stream に対して anyMatch を呼ぶと、結果は false です。
「1 つも要素がないので、条件を満たすものも 1 つもない」という意味です。

一方、空の Stream に対して allMatch を呼ぶと、結果は true になります。

これは一見不思議ですが、
「反例が 1 つも存在しないので、“すべてが条件を満たしている”とみなす」
という論理(空集合に対する全称命題)の考え方に基づいています。

初心者向けには、

「anyMatch は“1 つでもあれば true”だから、要素が 0 個なら false」
「allMatch は“全部そうなら true”で、“そうじゃないやつ”が 1 つもいなければ true」

と覚えておけば十分です。


設計の指針:allMatch / anyMatch をどう使うか

自分の中でのルールを持つ

実務で迷わないために、シンプルなルールを持っておくと楽です。

「全員が条件を満たしているか?」
「全部が OK か?」
allMatch

「誰か 1 人でも条件を満たしているか?」
「1 つでも OK があればいいか?」
anyMatch

そして、

「要素そのものが欲しいなら findFirst / findAny」
「真偽値だけ欲しいなら allMatch / anyMatch」

という切り分けを意識しておくと、
Stream の終端操作の選択がかなりスムーズになります。


まとめ:for 文からの書き換えイメージ

最後に、よくある for 文のパターンを、allMatch / anyMatch に書き換えてみます。

例えば、

boolean hasAdult(List<User> users) {
    for (User u : users) {
        if (u.getAge() >= 18) {
            return true;
        }
    }
    return false;
}
Java

これはそのまま、

boolean hasAdult(List<User> users) {
    return users.stream()
                .anyMatch(u -> u.getAge() >= 18);
}
Java

にできます。

また、

boolean allActive(List<User> users) {
    for (User u : users) {
        if (!u.isActive()) {
            return false;
        }
    }
    return true;
}
Java

は、

boolean allActive(List<User> users) {
    return users.stream()
                .allMatch(User::isActive);
}
Java

と書けます。

タイトルとURLをコピーしました