让我们考虑一个Parent
类,它只包含一个Integer
属性。
我创建了6个父类对象,并将一个对象作为null。然后我将这些对象添加到列表中。
我想通过Integer
属性的值检索相应的对象。
我使用了Java 8 Streams。
Predicate<Parent> predicate = e -> e.getId() == 100; // sample attribute value
result = list.stream().filter(predicate).collect(Collectors.toList());
但是我得到NullPointerException
,所以我编辑了代码:
list.stream().filter(h -> h!=null).filter(predicate).collect(Collectors.toList());
但是如果任何对象为null,我想抛出一个异常。如果列表中没有对象为null,那么我想从列表中检索相应的对象。
如何使用Java 8 Streams使用单个语句实现此目的?
答案 0 :(得分:14)
JB Nizet的回答没问题,但是它仅使用map
作为副作用而不是映射操作,这有点奇怪。当你只对某些事物的副作用感兴趣时,可以使用一种方法,例如抛出异常:peek
。
List<Parent> filtered =
list.stream()
.peek(Objects::requireNonNull)
.filter(predicate)
.collect(Collectors.toList());
如果你想要自己的例外,只需在那里放一个lambda:
List<Parent> filtered =
list.stream()
.peek(p -> { if (p == null) throw new MyException(); })
.filter(predicate)
.collect(Collectors.toList());
如果检查了您的例外情况,您可以预先检查空值,如果您不介意两次遍历列表。在您的情况下,这可能是最好的,但可能并非总是可行。
if (list.contains(null)) throw new MyCheckedException();
您还可以在流管道中抛出未经检查的异常,捕获它然后抛出已选中的异常:
try {
...
.peek(p -> { if (p == null) throw new MyException(); })
...
} catch (MyException exc) {
throw new MyCheckedException();
}
或者你可以采取优雅但有争议的道路并使用偷偷摸摸的方法。
但要小心!这种技术绕过了已检查的异常系统,您应该知道自己在做什么。请务必声明周围的方法抛出MyCheckedException
!如果你没有,编译器不会警告你,如果检查异常出现在不期望的地方,它可能会导致奇怪的错误。
@SuppressWarnings("unchecked")
public <T extends Throwable> void throwSneakily(Throwable t) throws T {
throw (T) t;
}
public void m() throws MyCheckedException {
List<Parent> filtered =
list.stream()
.peek(p -> { if (p == null) throwSneakily(new MyCheckedException()); })
.filter(predicate)
.collect(Collectors.toList());
}
答案 1 :(得分:3)
让我们从最简单的解决方案开始:
if(list.contains(null)) throw new MyException();
result = list.stream().filter(predicate).collect(Collectors.toList());
如果您怀疑列表包含null
,甚至有一个专门的异常类型来标记这种情况,那么预检是最干净的解决方案。这可以确保如果谓词更改为可以处理null
的内容或者在使用可能在遇到后续null
之前结束的短路流操作时,此类条件不会静默保留。
如果列表中null
的出现仍被视为不应发生的编程错误,但您只想更改异常类型(我无法想象这是真正的原因),可能只是抓住并翻译异常:
try {
result = list.stream().filter(predicate).collect(Collectors.toList());
}
catch(NullPointerException ex) {
if(list.contains(null)) // ensure that we don’t hide another programming error
throw new MyException();
else throw ex;
}
在假设null
引用不的情况下,这是有效的。如上所述,如果您怀疑列表包含null
,您应该更喜欢预先检查。
答案 2 :(得分:1)
Function<Parent, Parent> throwingIdentity = p -> {
if (p == null) {
throw new MyException();
}
return p;
};
List<Parent> filtered =
list.stream()
.map(throwingIdentity)
.filter(predicate)
.collect(Collectors.toList());
答案 3 :(得分:0)
list.stream()
.map(Objects::requireNonNull)
.filter(predicate)
此代码清楚地将验证部分和过滤器部分分开。在异常调用堆栈中它将包含“ requireNonNull”帧。尽管通过java.util设计,它将增加NPEexceptoin类型。参考:javadoc。 SRC:openjdk
list.stream()
.map(item -> requireNonNull(item, "Null item in the list."))
.filter(predicate)
导入静态java.util.Objects.requireNonNull;
list.stream()
.map(item -> requireNonNull(item, () -> "Expected non-null items. Was: " + list))
.filter(predicate)
list.stream()
.map(this::requireItemNonNull)
.filter(predicate)
...
private Parent requireItemNonNull(Parent item) {
if (null == item) throw new IllegalStateException("Expected: non null items. Was: " + this.list);
return item;
如果只需要验证所有项目,请在最后使用收集器。例如。 toArray
作为流将实际迭代推迟到“收集”时间:
list.stream()
.map(this::validateItemNonNull)
.toArray();
注意::在生产代码中使用 Stream#map
,并且从不使用 Stream#peek
方法。正如Javadoc所说, peek 是 mainly to support debugging ...where the stream implementation is able to optimize away..., the action will not be invoked for those elements.
REF:javadoc