我试图使用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(很好!)

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


当前回答

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

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

其他回答

如果你不介意使用第三方库,来自cyclops-streams的SequenceM(和来自simple-react的LazyFutureStream)都有single和singleOptional操作符。

如果流中有0个或多个元素,singleOptional()将抛出异常,否则将返回单个值。

String result = SequenceM.of("x")
                          .single();

SequenceM.of().single(); // NoSuchElementException

SequenceM.of(1, 2, 3).single(); // NoSuchElementException

String result = LazyFutureStream.fromStream(Stream.of("x"))
                          .single();

如果流中没有值或有多个值,singleOptional()返回Optional.empty()。

Optional<String> result = SequenceM.fromStream(Stream.of("x"))
                          .singleOptional(); 
//Optional["x"]

Optional<String> result = SequenceM.of().singleOptional(); 
// Optional.empty

Optional<String> result =  SequenceM.of(1, 2, 3).singleOptional(); 
// Optional.empty

披露-我是这两个库的作者。

使用收集器:

public static <T> Collector<T, ?, Optional<T>> singleElementCollector() {
    return Collectors.collectingAndThen(
            Collectors.toList(),
            list -> list.size() == 1 ? Optional.of(list.get(0)) : Optional.empty()
    );
}

用法:

Optional<User> result = users.stream()
        .filter((user) -> user.getId() < 0)
        .collect(singleElementCollector());

我们返回一个Optional,因为我们通常不能假设集合只包含一个元素。如果你已经知道这是什么情况,请致电:

User user = result.orElseThrow();

这就把处理错误的负担放在了调用者身上——这是应该的。

使用Reduce和Optional

来自Fabio Bonfante的回应:

public <T> T getOneExample(Collection<T> collection) {
    return collection.stream()
        .filter(x -> /* do some filter */)
        .reduce((x,y)-> {throw new IllegalStateException("multiple");})
        .orElseThrow(() -> new NoSuchElementException("none"));
}
User match = users.stream().filter((user) -> user.getId()== 1).findAny().orElseThrow(()-> new IllegalArgumentException());

受到@skiwi的启发,我用下面的方法解决了这个问题:

public static <T> T toSingleton(Stream<T> stream) {
    List<T> list = stream.limit(1).collect(Collectors.toList());
    if (list.isEmpty()) {
        return null;
    } else {
        return list.get(0);
    }
}

然后:

User user = toSingleton(users.stream().filter(...).map(...));