我试图使用Java 8流在LinkedList中查找元素。但是,我想保证与筛选条件有且只有一个匹配。

以这段代码为例:

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;
    }
}

这段代码根据用户的ID查找用户。但是不能保证有多少用户匹配过滤器。

更改过滤器行为:

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

将抛出一个NoSuchElementException(很好!)

但是,如果有多个匹配,我希望它抛出一个错误。有办法做到这一点吗?


当前回答

使用减少

这是我发现的更简单灵活的方法(基于@prunge的答案)

Optional<User> user = users.stream()
        .filter(user -> user.getId() == 1)
        .reduce((a, b) -> {
            throw new IllegalStateException("Multiple elements: " + a + ", " + b);
        })

这样你就可以得到:

Optional -和你的对象一样,如果不存在则使用Optional.empty() 如果有多个元素,则使用Exception(最终使用YOUR自定义类型/消息)

其他回答

Guava有一个名为MoreCollectors.onlyElement()的收集器。

你试过这个吗

long c = users.stream().filter((user) -> user.getId() == 1).count();
if(c > 1){
    throw new IllegalStateException();
}

long count()
Returns the count of elements in this stream. This is a special case of a reduction and is equivalent to:

     return mapToLong(e -> 1L).sum();

This is a terminal operation.

来源:https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html

我正在使用这两个收集器:

public static <T> Collector<T, ?, Optional<T>> zeroOrOne() {
    return Collectors.reducing((a, b) -> {
        throw new IllegalStateException("More than one value was returned");
    });
}

public static <T> Collector<T, ?, T> onlyOne() {
    return Collectors.collectingAndThen(zeroOrOne(), Optional::get);
}
 List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
Integer value  = list.stream().filter((x->x.intValue()==8)).findFirst().orElse(null);

我已经使用整数类型而不是原语,因为它将有空指针异常。你只需要处理这个异常…看起来很简洁,我觉得;)

让你做一些流不支持的奇怪事情的“逃生舱口”操作是请求一个Iterator:

Iterator<T> it = users.stream().filter((user) -> user.getId() < 0).iterator();
if (!it.hasNext()) {
    throw new NoSuchElementException();
} else {
    result = it.next();
    if (it.hasNext()) {
        throw new TooManyElementsException();
    }
}

Guava有一个方便的方法来获取一个Iterator并获取唯一的元素,如果有零个或多个元素就抛出,这可以替换这里底部的n-1行。