我试图使用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(很好!)
但是,如果有多个匹配,我希望它抛出一个错误。有办法做到这一点吗?
如果你不使用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的要点
创建一个自定义收集器
public static <T> Collector<T, ?, T> toSingleton() {
return Collectors.collectingAndThen(
Collectors.toList(),
list -> {
if (list.size() != 1) {
throw new IllegalStateException();
}
return list.get(0);
}
);
}
我们使用收集工具。然后构造我们想要的收集器
使用collector. tolist()收集器在列表中收集对象。
在最后应用一个额外的结束符,返回单个元素—或者抛出一个IllegalStateException if列表。Size != 1。
用作:
User resultUser = users.stream()
.filter(user -> user.getId() > 0)
.collect(toSingleton());
然后,您可以随心所欲地定制这个Collector,例如在构造函数中将异常作为参数,将其调整为允许两个值,甚至更多。
另一种可能不那么优雅的解决方案是:
您可以使用包含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);
为了完整起见,下面是@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));
如果你不介意使用第三方库,来自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 List<state> getAllActiveState() {
List<Master> master = masterRepository.getActiveExamMasters();
Master activeMaster = new Master();
try {
activeMaster = master.stream().filter(status -> status.getStatus() == true).reduce((u, v) -> {
throw new IllegalStateException();
}).get();
return stateRepository.getAllStateActiveId(activeMaster.getId());
} catch (IllegalStateException e) {
logger.info(":More than one status found TRUE in Master");
return null;
}
}
In this above code, As per the condition if its find more than one true in the list then it will through the exception.
When it through the error will showing custom message because it easy maintain the logs on server side.
From Nth number of element present in list just want only one element have true condition if in list there are more than one elements having true status at that moment it will through an exception.
after getting all the this we using get(); to taking that one element from list and stored it into another object.
If you want you added optional like Optional<activeMaster > = master.stream().filter(status -> status.getStatus() == true).reduce((u, v) -> {throw new IllegalStateException();}).get();