我试图使用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的精彩回答对应的“一行”:
User user1 = users.stream()
.filter(user -> user.getId() == 1)
.reduce((a, b) -> {
throw new IllegalStateException("Multiple elements: " + a + ", " + b);
})
.get();
这将从流中获得唯一匹配的元素,即抛出
NoSuchElementException,如果流是空的,或者
IllegalStateException,如果流包含多个匹配元素。
这种方法的一种变体可以避免过早抛出异常,而是将结果表示为一个包含唯一元素的Optional,如果有零个或多个元素,则什么都不包含(空):
Optional<User> user1 = users.stream()
.filter(user -> user.getId() == 1)
.collect(Collectors.reducing((a, b) -> null));
更新
@Holger的评论建议不错:
Optional<User> match = users.stream()
.filter((user) -> user.getId() > 1)
.reduce((u, v) -> { throw new IllegalStateException("More than one ID found") });
原来的答案
异常由Optional#get抛出,但如果有多个元素,则没有帮助。你可以在一个只接受一个项的集合中收集用户,例如:
User match = users.stream().filter((user) -> user.getId() > 1)
.collect(toCollection(() -> new ArrayBlockingQueue<User>(1)))
.poll();
它会抛出一个java.lang.IllegalStateException:队列已满,但感觉太粗糙了。
或者你可以使用减法和可选的结合:
User match = Optional.ofNullable(users.stream().filter((user) -> user.getId() > 1)
.reduce(null, (u, v) -> {
if (u != null && v != null)
throw new IllegalStateException("More than one ID found");
else return u == null ? v : u;
})).get();
约简的结果是:
如果没有找到用户,则为Null
如果只找到一个,则返回用户
如果发现多个异常,则抛出异常
然后将结果包装在可选的。
但最简单的解决方案可能是收集到一个集合,检查它的大小为1,并获得唯一的元素。
如果你不使用Guava或Kotlin,这里有一个基于@skiwi和@Neuron答案的解决方案。
users.stream().collect(single(user -> user.getId() == 1));
or
users.stream().collect(optional(user -> user.getId() == 1));
其中single和optional是返回相应收集器的静态导入函数。
我认为,如果将过滤逻辑移到收集器内部,看起来会更简洁。同样,如果您碰巧用.filter删除字符串,代码中也不会中断任何内容。
代码https://gist.github.com/overpas/ccc39b75f17a1c65682c071045c1a079的要点