为什么我不能使用过滤器作为流中的最后一步



我一直被告知,不通过收集和查找First等方法终止流是一种不好的做法,但没有真正的反馈,为什么在博客中没有太多关于它的说法。

查看以下示例,我没有使用大量嵌套的 if 检查,而是使用 Optional 来返回一个列表值。如您所见,我的最后一步是该流中的过滤器。这对我来说是预期的,那就是取回一个列表。为什么这是错误的,我应该如何写它?

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
}

我一直被告知不通过以下方式终止流是一种不好的做法 诸如收集和查找第一之类的方法,但没有关于原因的真实反馈 在博客中没有太多关于它的说法。

这实际上取决于上下文,如果您说"我可以用中间操作结束流吗,例如filter并且永远不要调用终端操作(消耗流的操作("那么是的,这是不好的做法,而且毫无意义,因为您刚刚定义了一些标准,但从未要求"结果"。

流是惰性的,因为它们不会做任何事情,除非终端操作告诉它们,例如collectfindFirst等。

如果你说"从方法返回流是不是一种不好的做法",那么 关于是否应该返回流或集合的答案可能值得一读。

此外,请注意,您的getBeanViaOptional逻辑是在Optional<T>而不是Stream<T>上运行的。是的,它们都有mapflatMapfilter但请注意,一个Optional<T>只能包含一个值,或者它是的,而一个流可以有一个或多个

您在可读性、维护等方面使用 Optional 而不是命令式ifs 的方法显然更好,所以我建议您继续使用这种方法,尽管您可以通过使用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);

对于流,通常当没有终端操作时,不会执行任何中间操作。您的示例使用Optional.其操作mapfilter与流中的某些中间操作具有相同的名称,但它们是不同的。你的例子在你的问题提出的那一行是可以的(不是坏的做法(。

另一件事是(正如 Aomine 已经指出的那样(.orElseThrow是在Optional中获取值并在没有值时抛出异常的较短方法。更重要的是,使用.orElseThrow更安全 (如果有默认值,则.orElse(。 应尽可能避免Optional.get()。如果没有值,您将获得NoSuchElementException。这几乎和不使用Optional时获得NullPointerException一样糟糕。Optional以正确的方式使用可以保护您免受NullPointerException

最新更新