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);