代码

 public static void main (String[] args) {
Stream.of("d2", "a2", "b1", "b3", "c")
.sorted((s1, s2) -> {
System.out.printf("sort: %s; %s\n", s1, s2);
return s1.compareTo(s2);
})
.forEach(System.out::println);
}

执行结果

sort: a2; d2
sort: b1; a2
sort: b1; d2
sort: b1; a2
sort: b3; b1
sort: b3; d2
sort: c; b3
sort: c; d2
a2
b1
b3
c
d2

看到结果不淡定了,因此决定调试一下看看内部包装了哪种排序算法,这一调试不得了,发现stream的调用链有点奇怪:

以上这段代码利用了java8中的stream概念,在实际调试过程中,你会发现并不能从sorted()这里直接进入排序部分,由此引出本文。

分析

如果把上面代码中的 .forEach(System.out::println) 去掉,你会发现sorted()函数会被忽略因而根本不会执行,这就涉及到了stream的执行原理。

调用链记录为两大部分:

第一部分:sorted()

/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/src.zip!/java/util/stream/ReferencePipeline.java
  
public final Stream<P_OUT> sorted(Comparator<? super P_OUT> comparator) {
return SortedOps.makeRef(this, comparator);
}
/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/src.zip!/java/util/stream/SortedOps.java

static <T> Stream<T> makeRef(AbstractPipeline<?, T, ?> upstream,
Comparator<? super T> comparator) {
return new OfRef<>(upstream, comparator);
} OfRef(AbstractPipeline<?, T, ?> upstream, Comparator<? super T> comparator) {
super(upstream, StreamShape.REFERENCE,
StreamOpFlag.IS_ORDERED | StreamOpFlag.NOT_SORTED);
this.isNaturalSort = false;
this.comparator = Objects.requireNonNull(comparator);
}

第二部分:foreach()

/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/src.zip!/java/util/stream/ReferencePipeline.java

public void forEach(Consumer<? super P_OUT> action) {
evaluate(ForEachOps.makeRef(action, false));
}
/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/src.zip!/java/util/stream/AbstractPipeline.java

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()));
}
/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/src.zip!/java/util/stream/ForEachOps.java

public <S> Void evaluateSequential(PipelineHelper<T> helper,
Spliterator<S> spliterator) {
return helper.wrapAndCopyInto(this, spliterator).get();
}
/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/src.zip!/java/util/stream/AbstractPipeline.java
final <P_IN, S extends Sink<E_OUT>> S wrapAndCopyInto(S sink, Spliterator<P_IN> spliterator) {
copyInto(wrapSink(Objects.requireNonNull(sink)), spliterator);
return sink;
}
final <P_IN> Sink<P_IN> wrapSink(Sink<E_OUT> sink) {
Objects.requireNonNull(sink); for ( @SuppressWarnings("rawtypes") AbstractPipeline p=AbstractPipeline.this; p.depth > 0; p=p.previousStage) {
sink = p.opWrapSink(p.previousStage.combinedFlags, sink);
}
return (Sink<P_IN>) sink;
}
/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/src.zip!/java/util/stream/SortedOps.java

public Sink<T> opWrapSink(int flags, Sink<T> sink) {
Objects.requireNonNull(sink); // If the input is already naturally sorted and this operation
// also naturally sorted then this is a no-op
if (StreamOpFlag.SORTED.isKnown(flags) && isNaturalSort)
return sink;
else if (StreamOpFlag.SIZED.isKnown(flags))
return new SizedRefSortingSink<>(sink, comparator);
else
return new RefSortingSink<>(sink, comparator);
}
SizedRefSortingSink(Sink<? super T> sink, Comparator<? super T> comparator) {
super(sink, comparator);
}
AbstractRefSortingSink(Sink<? super T> downstream, Comparator<? super T> comparator) {
super(downstream);
this.comparator = comparator;
}
 /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/src.zip!/java/util/stream/Sink.java

public ChainedReference(Sink<? super E_OUT> downstream) {
this.downstream = Objects.requireNonNull(downstream);
}
/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/src.zip!/java/util/stream/AbstractPipeline.java

final <P_IN> void copyInto(Sink<P_IN> wrappedSink, Spliterator<P_IN> spliterator) {
Objects.requireNonNull(wrappedSink); if (!StreamOpFlag.SHORT_CIRCUIT.isKnown(getStreamAndOpFlags())) {
wrappedSink.begin(spliterator.getExactSizeIfKnown());
spliterator.forEachRemaining(wrappedSink);
wrappedSink.end();
}
else {
copyIntoWithCancel(wrappedSink, spliterator);
}
}
/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/src.zip!/java/util/stream/SortedOps.java

public void end() {
Arrays.sort(array, 0, offset, comparator);
downstream.begin(offset);
if (!cancellationWasRequested) {
for (int i = 0; i < offset; i++)
downstream.accept(array[i]);
}
else {
for (int i = 0; i < offset && !downstream.cancellationRequested(); i++)
downstream.accept(array[i]);
}
downstream.end();
array = null;
}
/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/src.zip!/java/util/Arrays.java

public static <T> void sort(T[] a, int fromIndex, int toIndex,
Comparator<? super T> c) {
if (c == null) {
sort(a, fromIndex, toIndex);
} else {
rangeCheck(a.length, fromIndex, toIndex);
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, fromIndex, toIndex, c);
else
TimSort.sort(a, fromIndex, toIndex, c, null, 0, 0);
}
}
/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/src.zip!/java/util/TimSort.java

static <T> void sort(T[] a, int lo, int hi, Comparator<? super T> c,
T[] work, int workBase, int workLen) {
assert c != null && a != null && lo >= 0 && lo <= hi && hi <= a.length; int nRemaining = hi - lo;
if (nRemaining < 2)
return; // Arrays of size 0 and 1 are always sorted // If array is small, do a "mini-TimSort" with no merges
if (nRemaining < MIN_MERGE) {
int initRunLen = countRunAndMakeAscending(a, lo, hi, c);
binarySort(a, lo, hi, lo + initRunLen, c);
return;
} /**
* March over the array once, left to right, finding natural runs,
* extending short natural runs to minRun elements, and merging runs
* to maintain stack invariant.
*/
TimSort<T> ts = new TimSort<>(a, c, work, workBase, workLen);
int minRun = minRunLength(nRemaining);
do {
// Identify next run
int runLen = countRunAndMakeAscending(a, lo, hi, c); // If run is short, extend to min(minRun, nRemaining)
if (runLen < minRun) {
int force = nRemaining <= minRun ? nRemaining : minRun;
binarySort(a, lo, lo + force, lo + runLen, c);
runLen = force;
} // Push run onto pending-run stack, and maybe merge
ts.pushRun(lo, runLen);
ts.mergeCollapse(); // Advance to find next run
lo += runLen;
nRemaining -= runLen;
} while (nRemaining != 0); // Merge all remaining runs to complete sort
assert lo == hi;
ts.mergeForceCollapse();
assert ts.stackSize == 1;
}

第3部分:compareTo()

这部分就不贴了,比较器

从以上调用链可以看出,sorted()之后首先进入foreach(),然后在foreach()中调用了Timsort()排序算法,最后又调用比较器;很明显,这个调用过程和程序顺序不一样。由于刚刚接触,就先把调用链记录下来,以后再补充深层原理。


												

java8 Stream sorted()的一次调用链记录的更多相关文章

  1. java8 stream sorted

    1.对象类型配列 List<Person> list = Arrays.asList( new Person(22, "shaomch", "man" ...

  2. JAVA8 之 Stream sorted() 示例

    下面代码以自然序排序一个listlist.stream().sorted() 自然序逆序元素,使用Comparator 提供的reverseOrder() 方法list.stream().sorted ...

  3. Java8 使用 stream().sorted()对List集合进行排序

    集合对像定义 集合对象以学生类(StudentInfo)为例,有学生的基本信息,包括:姓名,性别,年龄,身高,生日几项. 使用stream().sorted()进行排序,需要该类实现 Comparab ...

  4. JAVA8 Stream集合操作:中间方法和完结方法

    StreamLambda为java8带了闭包,这一特性在集合操作中尤为重要:java8中支持对集合对象的stream进行函数式操作,此外,stream api也被集成进了collection api, ...

  5. 【转】Java8 Stream 流详解

      当我第一次阅读 Java8 中的 Stream API 时,说实话,我非常困惑,因为它的名字听起来与 Java I0 框架中的 InputStream 和 OutputStream 非常类似.但是 ...

  6. Java8 Stream流

    第三章 Stream流 <Java8 Stream编码实战>的代码全部在https://github.com/yu-linfeng/BlogRepositories/tree/master ...

  7. Java8 Stream代码详解+BenchMark测试

    Java8 Stream基础.深入.测试 1.基本介绍 1.创建方式 1.Array的Stream创建 1.直接创建 // main Stream stream = Stream.of("a ...

  8. java8 Stream常用方法和特性浅析

    有一个需求,每次需要将几万条数据从数据库中取出,并根据某些规则,逐条进行业务处理,原本准备批量进行for循环或者使用存储过程,但是for循环对于几万条数据来说效率较低:存储过程因为逻辑非常复杂,写起来 ...

  9. java代码之美(2)---Java8 Stream

    Stream 第一次看到Stream表达式就深深把我吸引,用它可以使你的代码更加整洁而且对集合的操作效率也会大大提高,如果你还没有用到java8的Stream特性,那就说明你确实out啦. 一.概述 ...

随机推荐

  1. 爱上python之盲注探测脚本

    本文转自:i春秋论坛       前言: 最近在学python,做了个盲注的简单的跑用户的脚本,仅做个记录. sqmap也有不灵的时候,有时需要根据情况自写脚本探测 正文: 本地用大表姐给的sql和p ...

  2. 网络编程懒人入门(九):通俗讲解,有了IP地址,为何还要用MAC地址?

    1.前言 标题虽然是为了解释有了 IP 地址,为什么还要用 MAC 地址,但是本文的重点在于理解为什么要有 IP 这样的东西.本文对读者的定位是知道 MAC 地址是什么,IP 地址是什么. (本文同步 ...

  3. JDK8 新增的日期时间API

    背景 JDK8中增加了一套全新的日期时间API,这里进行总结下,方便查询使用. 新的时间及日期API位于 java.time 包中,下面是一些关键类. Instant:代表的是时间戳. LocalDa ...

  4. java 堆 栈 方法区的简单分析

    Java里的堆(heap)栈(stack)和方法区(method) 基础数据类型直接在栈空间分配, 方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收.   引用数据类型,需要用new来创 ...

  5. idea src下源文件和class编译文件不一致

    今天遇到一个神奇BUG,一个和elasticsearch没有任何关系的项目,报错ES某个包找不到,刚开始以为是依赖了父项目的某个包,并且本项目主启动类ComponentScan扫描了相关的类进入Spr ...

  6. 百度翻译爬虫-Web版(自动生成sign)

    # 面向对象 # 百度翻译 -- 网页版(自动获取token,sign) import requests import js2py import json import re class WebFan ...

  7. 《CLR Via C#》读书笔记:26.线程基础

    一.线程开销 操作系统创建线程是有代价的,其主要开销在下面列举出来了. 内存开销 线程内核对象 拥有线程描述属性与线程上下文,线程上下文占用的内存空间为 x86 架构 占用 700 字节.x64 架构 ...

  8. 使用Docker发布DNC项目

    项目结构 可以忽略中间三个Console项目 ApiCenter 是一个WebAPI项目,引用了NLog.MQ项目 ApiCenter使用5001端口 public static IWebHost B ...

  9. mysql 开发基础系列18 存储过程和函数(下)

    1. 光标的使用(游标) 在存储过程和函数中可以使用光标对结果集进行循环的处理,光标使用包括光标的声明,open ,fetch,close. 下面在存储过程中使用一个光标, 这个举例中光标里的逻辑不重 ...

  10. ArcMap中属性字段计算器(Field Calculator)的使用技巧

           很多时候,我们在使用ArcGIS进行属性数据编辑时,需要批量修改某些字段值,这时候ArcGIS提供的属性字段计算器(Field Calculator)就是一把利器.下面我就Field C ...