1. ホーム

[解決済み】Javaストリームを1つの要素だけにフィルタリングする

2022-03-31 16:20:42

質問

Java 8を使おうとしています Stream の中にある要素を見つけるために LinkedList . しかし、私は、フィルタ条件に一致するものが1つだけであることを保証したいと思います。

このコードを見てください。

public static void main(String[] args) {

    LinkedList<User> users = new LinkedList<>();
    users.add(new User(1, "User1"));
    users.add(new User(2, "User2"));
    users.add(new User(3, "User3"));

    User match = users.stream().filter((user) -> user.getId() == 1).findAny().get();
    System.out.println(match.toString());
}


static class User {

    @Override
    public String toString() {
        return id + " - " + username;
    }

    int id;
    String username;

    public User() {
    }

    public User(int id, String username) {
        this.id = id;
        this.username = username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public int getId() {
        return id;
    }
}

このコードでは User をそのIDを元に作成します。しかし、いくつの User がフィルタにマッチした。

フィルター行を変更する。

User match = users.stream().filter((user) -> user.getId() < 0).findAny().get();

を投げます。 NoSuchElementException (良い!)

複数マッチした場合はエラーを投げるようにしてほしいのですが。そのような方法はありますか?

どのように解決するのですか?

カスタムを作成する Collector

public static <T> Collector<T, ?, T> toSingleton() {
    return Collectors.collectingAndThen(
            Collectors.toList(),
            list -> {
                if (list.size() != 1) {
                    throw new IllegalStateException();
                }
                return list.get(0);
            }
    );
}

を使用します。 Collectors.collectingAndThen を作成し、目的の Collector によって

  1. オブジェクトを集めて List と共に Collectors.toList() コレクターになります。
  2. 最後に追加のフィニッシャーを適用して、単一の要素を返すか、あるいは IllegalStateException もし list.size != 1 .

として使用されます。

User resultUser = users.stream()
        .filter(user -> user.getId() > 0)
        .collect(toSingleton());

そして、これをカスタマイズすることができます。 Collector 例えばコンストラクタの引数として例外を与えたり、 値を 2 つ指定できるようにしたりと、好きなようにカスタマイズできます。

もう一つの解決策-間違いなく、よりエレガントではない-。

を含む「ワークアラウンド」を使用することができます。 peek()AtomicInteger しかし、本当はそれを使うべきではありません。

代わりにできることは、それを List のような、このような。

LinkedList<User> users = new LinkedList<>();
users.add(new User(1, "User1"));
users.add(new User(2, "User2"));
users.add(new User(3, "User3"));
List<User> resultUserList = users.stream()
        .filter(user -> user.getId() == 1)
        .collect(Collectors.toList());
if (resultUserList.size() != 1) {
    throw new IllegalStateException();
}
User resultUser = resultUserList.get(0);