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

kar*_*kar 8 java java-8

我一直被告知,不通过诸如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
}
Run Code Online (Sandbox Code Playgroud)

Ous*_* D. 10

我一直被告知,不通过诸如collect和findFirst之类的方法终止Stream是一种不好的做法,但是没有真正的反馈来说明为什么在博客中对此不多说。

它确实取决于上下文,如果您说的是“我是否可以使用中间操作来结束流,例如,filter并且从未调用终端操作(消耗该流的操作)那么是的,这是一种不好的做法,而且毫无意义,因为您刚刚定义了一些标准,但从未要求提供“结果”。

流是在这个意义上,他们没有做任何事情,除非偷懒由终端操作如告诉如此collectfindFirst等等。

如果您说的是“从方法中返回流是否是错误的做法”,那么可能值得阅读有关应返回流还是集合的答案。

此外,请注意,您的getBeanViaOptional逻辑是在Optional<T>而不是上操作的Stream<T>。是的,他们都有mapflatMap而且filter但请注意,一个Optional<T>只能包含一个值,或者是空的,而流可以有一个或多个。

if在可读性,维护性等方面,使用Optional代替命令式s的方法显然更好。因此,我建议您继续使用该方法,尽管您可以通过使用orElseThrowie 进行一些改进:

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);
Run Code Online (Sandbox Code Playgroud)


Don*_*nat 7

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

另一件事是(如Aomine所指出的).orElseThrow是在中获取值并在Optional没有值时引发异常的较短方法。更重要的是它使用起来更安全.orElseThrow (或者.orElse有默认值)。Optional.get()应尽可能避免。NoSuchElementException如果没有价值,您将得到一个。这与NullPointerException不使用时获得几乎相同OptionalOptional正确使用可以保护您免受伤害NullPointerException