关于Optional#filter和Stream#filter的结果差异原因分析

本文探讨了在使用Optional#filter和Stream#filter时遇到的结果不一致问题。通过实验展示了在不同场景下,两个方法的过滤操作如何导致不同的输出。实验表明,关键在于Optional#filter和Stream#filter的参数类型不同,导致了最终结论的差异。
今天遇到一个问题: 发现 Optional#filter 和 Stream#filter返回的结果不一致?
做个简单小实验

给定一个List 类型的数据集 strList1, 通过filter过滤集合中不包含在 strList2的结果集 resList;

数据集
List<String> strList1 = Arrays.asList("lizhaoyang", "zhaoyang", "softsun", "lzy");
List<String> strList2 = Arrays.asList("lizhaoyang", "lzy");
// 期望结果
List<String> expectList = Arrays.asList("zhaoyang", "softsun");
实验1: 通过 Optional#filter 来实现
List<String> realList1 = Optional.ofNullable(strList1).
    filter(l -> !strList2.contains(l)).orElse(Collections.emptyList());
实验2: 通过Stream#filter来实现
List<String> realList2 = strList1.stream().filter(l -> !strList2.contains(l))
    .collect(Collectors.toList());
实验结论

:::info
java.util.Optional#filter res: false
java.util.stream.Stream#filter res: true
:::

完整代码
public class FilterDemo {

    public void test1() {
        List<String> strList1 = Arrays.asList("lizhaoyang", "zhaoyang", "softsun", "lzy");
        List<String> strList2 = Arrays.asList("lizhaoyang", "lzy");
        // 期望结果
        List<String> expectList = Arrays.asList("zhaoyang", "softsun");
        List<String> realList1 = Optional.ofNullable(strList1).
                filter(l -> !strList2.contains(l)).orElse(Collections.emptyList());

//        org.apache.commons.collections4.CollectionUtils#isEqualCollection(java.util.Collection<?>, java.util.Collection<?>)
        boolean assertRes = CollectionUtils.isEqualCollection(realList1, expectList);
        System.out.println("java.util.Optional#filter res: " + assertRes);
    }

    public void test2() {
        List<String> strList1 = Arrays.asList("lizhaoyang", "zhaoyang", "softsun", "lzy");
        List<String> strList2 = Arrays.asList("lizhaoyang", "lzy");
        // 期望结果
        List<String> expectList = Arrays.asList("zhaoyang", "softsun");
        List<String> realList2 = strList1.stream().filter(l -> !strList2.contains(l))
                .collect(Collectors.toList());

//        org.apache.commons.collections4.CollectionUtils#isEqualCollection(java.util.Collection<?>, java.util.Collection<?>)
        boolean assertRes = CollectionUtils.isEqualCollection(realList2, expectList);
        System.out.println("java.util.stream.Stream#filter res: " + assertRes);
    }

    public static void main(String[] args) {
        FilterDemo f = new FilterDemo();
        f.test1();
        f.test2();
    }
}
原因分析:

猜测原因: Optional#filter 和 Stream#filter 入参 类型不一致 或者 filter实现不一致;具体看源码, 发现其实没有差别; 那么具体是哪里的问题呢?

// java.util.Optional#filter
/**
     * If a value is present, and the value matches the given predicate,
     * return an {@code Optional} describing the value, otherwise return an
     * empty {@code Optional}.
     *
     * @param predicate a predicate to apply to the value, if present
     * @return an {@code Optional} describing the value of this {@code Optional}
     * if a value is present and the value matches the given predicate,
     * otherwise an empty {@code Optional}
     * @throws NullPointerException if the predicate is null
     */
    public Optional<T> filter(Predicate<? super T> predicate) {
        Objects.requireNonNull(predicate);
        if (!isPresent())
            return this;
        else
            return predicate.test(value) ? this : empty();
    }
//java.util.Arrays.ArrayList#indexOf
// o 是 strList1 ; a 是 strList2 数组;o.equals(a[i])是永远不可能相等的

@Override
        public int indexOf(Object o) {
            E[] a = this.a;
            if (o == null) {
                for (int i = 0; i < a.length; i++)
                    if (a[i] == null)
                        return i;
            } else {
                for (int i = 0; i < a.length; i++)
                    if (o.equals(a[i]))
                        return i;
            }
            return -1;
        }

// java.util.stream.Stream#filter
/**
     * Returns a stream consisting of the elements of this stream that match
     * the given predicate.
     *
     * <p>This is an <a href="package-summary.html#StreamOps">intermediate
     * operation</a>.
     *
     * @param predicate a <a href="package-summary.html#NonInterference">non-interfering</a>,
     *                  <a href="package-summary.html#Statelessness">stateless</a>
     *                  predicate to apply to each element to determine if it
     *                  should be included
     * @return the new stream
     */
    Stream<T> filter(Predicate<? super T> predicate);
//java.util.stream.Stream#filter 的实现
//java.util.stream.ReferencePipeline#filter
@Override
    public final Stream<P_OUT> filter(Predicate<? super P_OUT> predicate) {
        Objects.requireNonNull(predicate);
        return new StatelessOp<P_OUT, P_OUT>(this, StreamShape.REFERENCE,
                                     StreamOpFlag.NOT_SIZED) {
            @Override
            Sink<P_OUT> opWrapSink(int flags, Sink<P_OUT> sink) {
                return new Sink.ChainedReference<P_OUT, P_OUT>(sink) {
                    @Override
                    public void begin(long size) {
                        downstream.begin(-1);
                    }

                    @Override
                    public void accept(P_OUT u) {
                        if (predicate.test(u))
                            downstream.accept(u);
                    }
                };
            }
        };
    }

关于java.util.Optional#filter和java.util.stream.Stream#filter的入参其实都是 java.util.function.Predicate类型一致。但是注意 java.util.function.Predicate<T> T 是关键; Optional#filter 的T和Stream#filter 的 T不一致。 因此 可以得出一下结论。

结论

两者 filter的入参类型不一致;Optional#filter 的入参 l表示的是 List; 而 Stream#filter 的入参 l的类型是 String类型; 因此结论就不一致;
Optional#filter(l -> !strList2.contains(l)) -> List 类型
Stream#filter(l -> !strList2.contains(l)) -> String类型

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值