java8 stream接口终端操作allMatch 当list为空集合的一些思考

java8 stream接口终端操作allMatch 当list为空集合的一些思考


先看下如下代码

 public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        boolean allMatch = list.stream().allMatch(e -> e.equals("a"));
        boolean anyMatch = list.stream().anyMatch(e -> e.equals("a"));
        boolean noneMatch = list.stream().noneMatch(e -> e.equals("a"));
        System.out.println(allMatch);// true
        System.out.println(anyMatch);// false
        System.out.println(noneMatch);// true
    }

最近,有小伙伴留言说,boolean allMatch = list.stream().allMatch(e -> e.equals("a"));

当list的为空集合时候,这个返回默认为true;按照实际的业务,理解这个的话,应该为false;然后网上搜索了一下,比较尴尬的是,很多都是抄下我之前的文章,秉承着,严谨的原则,查看了源码,下面是整个分析的过程;

一、先简单查看源码,快速浏览一遍

查看源码

  	@Override
    public final boolean allMatch(Predicate<? super P_OUT> predicate) {
        return evaluate(MatchOps.makeRef(predicate, MatchOps.MatchKind.ALL));
    }

先看MatchOps.makeRef方法

   public static <T> TerminalOp<T, Boolean> makeRef(Predicate<? super T> predicate,
            MatchKind matchKind) {
        Objects.requireNonNull(predicate);
        Objects.requireNonNull(matchKind);
        class MatchSink extends BooleanTerminalSink<T> {
            MatchSink() {
                super(matchKind);
            }

            @Override
            public void accept(T t) {
                if (!stop && predicate.test(t) == matchKind.stopOnPredicateMatches) {
                    stop = true;
                    value = matchKind.shortCircuitResult;
                }
            }
        }

        return new MatchOp<>(StreamShape.REFERENCE, matchKind, MatchSink::new);
    }

最终返回的对象

new MatchOp<>(StreamShape.REFERENCE, matchKind, MatchSink::new);

再看对比的方法中的evaluate 方法

	@Override
    public final boolean allMatch(Predicate<? super P_OUT> predicate) {
        return evaluate(MatchOps.makeRef(predicate, MatchOps.MatchKind.ALL));
    }
   final <R> R evaluate(TerminalOp<E_OUT, R> terminalOp) {
            assert getOutputShape() == terminalOp.inputShape();
            if (linkedOrConsumed)
                throw new IllegalStateException(MSG_STREAM_LINKED);
            linkedOrConsumed = true;

            return isParallel()
                   ? terminalOp.evaluateParallel(this, 	sourceSpliterator(terminalOp.getOpFlags()))
                   : terminalOp.evaluateSequential(this, sourceSpliterator(terminalOp.getOpFlags()));
        }

直接看返回的方法

isParallel() 方法:

为true的时候:

terminalOp.evaluateParallel(this, sourceSpliterator(terminalOp.getOpFlags()))

为false的时候

terminalOp.evaluateSequential(this, sourceSpliterator(terminalOp.getOpFlags()))

先看 isParallel()方法

	@Override
    public final boolean isParallel() {
        //取的是一个成员变量
        return sourceStage.parallel;
    }

继续顺着代码点下去,可以看到如下:

  	private boolean parallel;
  
    AbstractPipeline(Supplier<? extends Spliterator<?>> source,
                     int sourceFlags, boolean parallel) {
        this.previousStage = null;
        this.sourceSupplier = source;
        this.sourceStage = this;
        this.sourceOrOpFlags = sourceFlags & StreamOpFlag.STREAM_MASK;
        // The following is an optimization of:
        // StreamOpFlag.combineOpFlags(sourceOrOpFlags, StreamOpFlag.INITIAL_OPS_VALUE);
        this.combinedFlags = (~(sourceOrOpFlags << 1)) & StreamOpFlag.INITIAL_OPS_VALUE;
        this.depth = 0;
        this.parallel = parallel;
    }

成员变量parallel是在构造方法中初始化的,查看什么地方调用了构造方法初始化,最终在java.util.stream.StreamSupport.class中,进行可以看到如下:

/**
     * Creates a new sequential or parallel {@code Stream} from a
     * {@code Supplier} of {@code Spliterator}.
     *
     * <p>The {@link Supplier#get()} method will be invoked on the supplier no
     * more than once, and only after the terminal operation of the stream pipeline
     * commences.
     *
     * <p>For spliterators that report a characteristic of {@code IMMUTABLE}
     * or {@code CONCURRENT}, or that are
     * <a href="../Spliterator.html#binding">late-binding</a>, it is likely
     * more efficient to use {@link #stream(java.util.Spliterator, boolean)}
     * instead.
     * <p>The use of a {@code Supplier} in this form provides a level of
     * indirection that reduces the scope of potential interference with the
     * source.  Since the supplier is only invoked after the terminal operation
     * commences, any modifications to the source up to the start of the
     * terminal operation are reflected in the stream result.  See
     * <a href="package-summary.html#NonInterference">Non-Interference</a> for
     * more details.
     *
     * @param <T> the type of stream elements
     * @param supplier a {@code Supplier} of a {@code Spliterator}
     * @param characteristics Spliterator characteristics of the supplied
     *        {@code Spliterator}.  The characteristics must be equal to
     *        {@code supplier.get().characteristics()}, otherwise undefined
     *        behavior may occur when terminal operation commences.
     * @param parallel if {@code true} then the returned stream is a parallel
     *        stream; if {@code false} the returned stream is a sequential
     *        stream.
     * @return a new sequential or parallel {@code Stream}
     * @see #stream(java.util.Spliterator, boolean)
     */
    public static <T> Stream<T> stream(Supplier<? extends Spliterator<T>> supplier,
                                       int characteristics,
                                       boolean parallel) {
        Objects.requireNonNull(supplier);
        return new ReferencePipeline.Head<>(supplier,
                                                  StreamOpFlag.fromCharacteristics(characteristics),
                                            parallel);
    }

这个参数定义,是否是并行流的意思;我们默认使用的stream(),是非并行流;

 boolean allMatch = list.stream().allMatch(e -> e.equals("a"));

二、详细步骤

看到这里,可能已经忘记了,上面说的什么,简单回顾一下,

	//第一步
	@Override
    public final boolean allMatch(Predicate<? super P_OUT> predicate) {
        return evaluate(MatchOps.makeRef(predicate, MatchOps.MatchKind.ALL));
    }

		//第二步  MatchOps.makeRef(predicate, MatchOps.MatchKind.ALL)
		// 返回 new MatchOp<>(StreamShape.REFERENCE, matchKind, MatchSink::new)
	  public static <T> TerminalOp<T, Boolean> makeRef(Predicate<? super T> predicate,
            MatchKind matchKind) {
        Objects.requireNonNull(predicate);
        Objects.requireNonNull(matchKind);
        class MatchSink extends BooleanTerminalSink<T> {
            MatchSink() {
                super(matchKind);
            }

            @Override
            public void accept(T t) {
                if (!stop && predicate.test(t) == matchKind.stopOnPredicateMatches) {
                    stop = true;
                    value = matchKind.shortCircuitResult;
                }
            }
        }

        return new MatchOp<>(StreamShape.REFERENCE, matchKind, MatchSink::new);
    }
	//第三步,isParallel()方法,我们使用的串行的流,所有返回false
	  final <R> R evaluate(TerminalOp<E_OUT, R> terminalOp) {
        assert getOutputShape() == terminalOp.inputShape();
        if (linkedOrConsumed)
            throw new IllegalStateException(MSG_STREAM_LINKED);
        linkedOrConsumed = true;

        return isParallel()
               ? terminalOp.evaluateParallel(this, sourceSpliterator(terminalOp.getOpFlags()))
               : terminalOp.evaluateSequential(this, sourceSpliterator(terminalOp.getOpFlags()));
    }

所以,我们直接看为false的情况;也就是

terminalOp.evaluateSequential(this, sourceSpliterator(terminalOp.getOpFlags()))

我们可以看到,这方法,使用第三步中的入参TerminalOp<E_OUT, R> terminalOp,也就是第二步中的返回方法;

再来看,第二步代码

 public static <T> TerminalOp<T, Boolean> makeRef(Predicate<? super T> predicate,
            MatchKind matchKind) {
        Objects.requireNonNull(predicate);// 校验参数
        Objects.requireNonNull(matchKind);// 校验参数
     	// 匿名对象,重写了BooleanTerminalSink类的accept方法
        class MatchSink extends BooleanTerminalSink<T> {
            MatchSink() {
                super(matchKind);
            }

            @Override
            public void accept(T t) {
                if (!stop && predicate.test(t) == matchKind.stopOnPredicateMatches) {
                    stop = true;
                    value = matchKind.shortCircuitResult;
                }
            }
        }
		/*
		StreamShape.REFERENCE 常量
		matchKind 上层传入的MatchOps.MatchKind.ALL
		MatchSink::new  这步骤,很关键,创建上面的匿名对象,调用空参的构造函数
		*/
        return new MatchOp<>(StreamShape.REFERENCE, matchKind, MatchSink::new);
    }

第二步代码,大家看注释,就可以了;

先看传入的参数MatchKind matchKind 是上一步的 MatchOps.MatchKind.ALL

  enum MatchKind {
        /** Do all elements match the predicate? */
        ANY(true, true),

        /** Do any elements match the predicate? */
        ALL(false, false),

        /** Do no elements match the predicate? */
        NONE(true, false);

        private final boolean stopOnPredicateMatches;
        private final boolean shortCircuitResult;

        private MatchKind(boolean stopOnPredicateMatches,
                          boolean shortCircuitResult) {
            this.stopOnPredicateMatches = stopOnPredicateMatches;
            this.shortCircuitResult = shortCircuitResult;
        }
    }

看最后一行的MatchSink::new,创建对象,调用空参构造方法,调用父类的构造方法。

 			MatchSink() {
                super(matchKind);
            }

父类的构造方法

  private static abstract class BooleanTerminalSink<T> implements Sink<T> {
        boolean stop;
        boolean value;

        BooleanTerminalSink(MatchKind matchKind) {
            //取到  MatchKind 枚举类为 ALL的 第二个参数   ALL(false, false), 也就是false,
            //取反,赋值给value 也就是默认value=true;
            value = !matchKind.shortCircuitResult;
        }

        public boolean getAndClearState() {
            return value;
        }

        @Override
        public boolean cancellationRequested() {
            return stop;
        }
    }

好了,大致的流程走完了,很关键的一步,已经出来了,value的值,初始化的时候,默认就为true

 BooleanTerminalSink(MatchKind matchKind) {
            //取到  MatchKind 枚举类为 ALL的 第二个参数   ALL(false, false), 也就是false,
            //取反,赋值给value 也就是默认value=true;
            value = !matchKind.shortCircuitResult;
        }

第三步代码,

final <R> R evaluate(TerminalOp<E_OUT, R> terminalOp) {
        assert getOutputShape() == terminalOp.inputShape();
        if (linkedOrConsumed)
            throw new IllegalStateException(MSG_STREAM_LINKED);
        linkedOrConsumed = true;

        return isParallel()
               ? terminalOp.evaluateParallel(this, sourceSpliterator(terminalOp.getOpFlags()))
               : terminalOp.evaluateSequential(this, sourceSpliterator(terminalOp.getOpFlags()));
    }

上述也说了,调用的是第二步 返回的对象return new MatchOp<>(StreamShape.REFERENCE, matchKind, MatchSink::new);中的evaluateSequential方法

 @Override
        public <S> Boolean evaluateSequential(PipelineHelper<T> helper,
                                              Spliterator<S> spliterator) {
            return helper.wrapAndCopyInto(sinkSupplier.get(), spliterator).getAndClearState();
        }

看返回的 sinkSupplier.get() ,代码看过取,发现,取的就是MatchSink::new对象,这个对象,默认的value就是true;最后一个.getAndClearState(),也是取的这个value值,作为返回值;

当我们的list集合中,size为0的时候,进去遍历的判断的时候,是遍历不进去的,取的默认值true;

三、最后的思考

最后,我们看两端代码,不使用java8的语法,面对集合判断,常规的两种,这么做的,或许,大家就更清楚这样的设计思想

  		List<String> list = new ArrayList<>();
        boolean falg1 = true;
        for (String string : list) {
            if (!string.equals("a")) {
                falg1 = false;
                break;
            }
        }
        System.out.println(falg1);
        /* -----------------------------------分割线-------------------- */
        int index = 0;
        boolean falg2 = false;
        for (String string : list) {
            if (string.equals("a")) {
                index++;
            } else {
                break;
            }
        }
        if (index == list.size()) {
            falg2 = true;
        }
        System.out.println(falg2);
  • 26
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

葵花下的獾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值