我一直被告知,不通过诸如collect和findFirst之类的方法终止Stream是一种不好的做法,但是却没有关于为什么在博客中对此不多说的真实反馈。
看下面的例子,我不是使用大量的嵌套if检查,而是使用Optional来获取List值。如您所见,我的最后一步是在该Stream中进行过滤。这对我来说是可以预期的,它将返回一个列表。为什么这是错误的,我应该怎么写呢?
import lombok.Getter;
import lombok.Setter;
import java.util.*;
public class Main {
public static void main(String[] args) {
RequestBean requestBean = new RequestBean();
// if I uncomment this I will get the list values printed as expected
// FruitBean fruitBean = new FruitBean();
// AnotherBean anotherBean = new AnotherBean();
// InnerBean innerBean = new InnerBean();
// requestBean.setFruitBeans(Collections.singletonList(fruitBean));
// fruitBean.setAnotherBeans(Collections.singletonList(anotherBean));
// anotherBean.setInnerBeans(Collections.singletonList(innerBean));
// List<String> beans = Arrays.asList("apple", "orange");
// innerBean.setBeans(beans);
List<String> result = getBeanViaOptional(requestBean);
if(result != null){
for(String s : result){
System.out.println(s);
}
}else {
System.out.println("nothing in list");
}
}
private static List<String> getBeanViaOptional(RequestBean bean){
Optional<List<String>> output = Optional.ofNullable(bean)
.map(RequestBean::getFruitBeans)
.map(n -> n.get(0))
.map(FruitBean::getAnotherBeans)
.map(n -> n.get(0))
.map(AnotherBean::getInnerBeans)
.map(n -> n.get(0))
.map(InnerBean::getBeans)
// why is this bad practice to end with a filter. how should I write this then?
.filter(n -> n.contains("apple"));
if(!output.isPresent()){
throw new CustomException();
}
return output.get();
}
// not using this. just to show that optional was preferable compared to this.
private static List<String> getBeanViaIfChecks(RequestBean bean){
if(bean != null){
if(bean.getFruitBeans() != null){
if(bean.getFruitBeans().get(0) != null){
if(bean.getFruitBeans().get(0).getAnotherBeans() != null){
if(bean.getFruitBeans().get(0).getAnotherBeans().get(0) != null){
if(bean.getFruitBeans().get(0).getAnotherBeans().get(0).getInnerBeans() != null){
if(bean.getFruitBeans().get(0).getAnotherBeans().get(0).getInnerBeans().get(0) != null){
return bean.getFruitBeans().get(0).getAnotherBeans().get(0).getInnerBeans().get(0).getBeans();
}
}
}
}
}
}
}
return null;
}
}
@Getter
@Setter
class RequestBean{
List<FruitBean> fruitBeans;
}
@Getter
@Setter
class FruitBean{
List<AnotherBean> anotherBeans;
}
@Getter
@Setter
class AnotherBean{
List<InnerBean> innerBeans;
}
@Getter
@Setter
class InnerBean{
List<String> beans;
}
class CustomException extends RuntimeException{
// do some custom exception stuff
}
答案 0 :(得分:10)
我不断被告知不要通过终止流 诸如collect和findFirst之类的方法,但没有关于其原因的真实反馈 在博客中对此没有多说。
这真的取决于上下文,如果您说的是“我可以使用中间操作(例如filter
结束流,而不调用终端操作(消耗了stream) ever ”(是),这是不正确的做法,毫无意义,因为您刚刚定义了一些条件,但从未要求过“结果”。
从某种意义上讲,流是懒惰的,除非终端操作告知它们什么都不做,例如collect
,findFirst
等
如果您说的是“从方法返回流是否是错误的做法”,那么 也许值得一读是否有一个should return a stream or a collection的答案。
此外,请注意,您的getBeanViaOptional
逻辑是在Optional<T>
而不是Stream<T>
上运行。是的,它们都有map
,flatMap
和filter
,但请注意,Optional<T>
只能包含一个值或空< / strong>,而一个流可以包含一个或多个。
在可读性,维护等方面,使用Optional而不是命令性if
的方法显然更好。因此,尽管可以稍加改进,我还是建议您继续使用该方法。通过使用orElseThrow
即:
return Optional.ofNullable(bean)
.map(RequestBean::getFruitBeans)
.map(n -> n.get(0))
.map(FruitBean::getAnotherBeans)
.map(n -> n.get(0))
.map(AnotherBean::getInnerBeans)
.map(n -> n.get(0))
.map(InnerBean::getBeans)
.filter(n -> n.contains("apple"))
.orElseThrow(CustomException::new);
答案 1 :(得分:7)
对于流,通常在没有终端操作时将不执行任何中间操作。您的示例使用Optional
。其操作map
和filter
与流中的某些中间操作具有相同的名称,但是它们是不同的。您的示例在您提出问题的那行是可以的(不是不好的做法)。
另一件事是(如Aomine所指出的那样).orElseThrow
是获取Optional
中的值并在没有异常时引发异常的较短方法。更重要的是,使用.orElseThrow
更安全
(如果有默认值,则为.orElse
)。尽可能避免使用Optional.get()
。如果没有价值,您将得到NoSuchElementException
。这与不使用NullPointerException
时获得Optional
几乎相同。正确使用Optional
可以保护您免受NullPointerException
的侵害。