1. 新时代的Java语言
新时代的Java是以JDK8的发布为分水岭,从JDK8开始,Java开发进入一个新的阶段,Java8 经历9个里程碑版本,于2014年3月18日正式发布,距今已有很长一段时间了,一些流行框架都采用了Java8,比如Mybatis3.5.x开始全面采用JDK8,Spring5.x开始全面采用JDK8,SpringBoot2.x开始全面采用JDK8,Spring Cloud F版开始也要求至少JDK8;
2. Java8有哪些新特性?
1、Lambda表达式;
2、方法引用;
3、默认方法;
4、可重复注解;
5、类型注解;
6、改进的类型推断;
7、方法参数反射;
8、Stream API;
9、Security增强;
10、JavaFX增强;
11、Tool增强;
12、国际化增强;
13、Deployment增强;
14、date-time日期包;
15、Nashorn javascript引擎;
16、Pack200压缩工具;
17、IO and NIO增强;
18、java.lang and java.util 包增强;
19、JDBC 4.2增强;
20、Java DB;
21、java.net包增强;
22、java.util.concurrent包增强;
23、Java XML - JAXP;
24、HotSpot增强;
25、Java Mission Control;
在Java8中最重要的两个新特性就要数Lambda表达式和Stream API了;
3. Stream API
Stream是Java 8中引入的全新API,位于java.util.stream包下,它与java.io 包下的 InputStream 和 OutputStream 等输入输出流是完全不同的概念;
Java 8 中的 Stream 是对数组、集合(Collection)功能的增强,它专注于对集合对象进行各种非常便利且高效的操作,它的底层实现使用了并行化操作,利用Java7 中引入的 Fork/Join 框架来拆分任务,加速处理过程,充分利用了现在多核处理器的优势;
需要强调的是Stream不对任何数据进行存储,所以Stream也不是数据结构,Stream实现的是快速地对集合类的数据源进行操作(如:过滤,排序、求和等等);
Stream
IntStream
DoubleStream
LongStream
Stream出现之前,遍历一个集合List、Array最传统的做法大概是用Iterator,或者 for 循环,这种两种方式都属于外部迭代,然而外部迭代存在着一些问题;
1、开发人员需要自己手写迭代的逻辑,大部分场景迭代逻辑都是每个元素遍历一次;
2、如果存在像排序、求最小值、求最大值这样的有状态的中间操作,不得不进行多次迭代;
Stream 处理数据的过程可以类别成工厂的流水线,数据可以看做流水线上的原料,对数据的操作可以看做流水线上的工人对原料的操作;
事实上Stream 只是一个接口,并没有操作的缺省实现,最主要的实现是
ReferencePipeline,而 ReferencePipeline 继承自AbstractPipeline,AbstractPipeline实现了BaseStream 接口并实现了它的方法,但ReferencePipeline 仍然是一个抽象类,因为它并没有实现所有的抽象方法,比如 AbstractPipeline 中的 opWrapSink,ReferencePipeline内部定义了三个静态内部类,分别是:Head, StatelessOp, StatefulOp,但只有 Head 不再是抽象类;
ReferencePipeline 包含了控制数据流入的 Head,中间操作 StatelessOp, StatefulOp,终止操作 TerminalOp;
Stream的工作流程
在Java 8中,Stream的生命周期一共有三个阶段:
1.获取数据源并创建Stream实例,数据源可以是数组、列表、对象或者I/O流;
2.执行中间操作(Intermediate Operations),中间操作可以是过滤、排序、类型转换等操作;
3.执行末端操作(Terminal Operation),末端操作主要是对最终结果进行计数、求和、创建新集合等操作;
Stream的操作大部分都是链式操作;
如何创建Stream实例?
1.使用数组创建Stream
2.使用List集合创建Stream
3.使用Builder方法创建Stream
参看代码演示;
Stream 常用的流操作包括:
中间操作(Intermediate Operations)
Stream的中间操作将返回一个Stream,它允许开发者以查询的方式调用多个其他的操作。特别注意的是,在“终端操作”方法未被调用之前,“中间操作”的方法不会被执行;
无状态(Stateless)操作:每个数据的处理是独立的,不会影响或依赖之前的数据。如filter()、flatMap()、flatMapToDouble()、flatMapToInt()、flatMapToLong()、map()、mapToDouble()、mapToInt()、mapToLong()、peek()、unordered() 等;
有状态(Stateful)操作:处理时会记录状态,比如处理了几个。后面元素的处理会依赖前面记录的状态,或者拿到所有元素才能继续下去,如
distinct()、sorted()、sorted(comparator)、limit()、skip() 等;
终止操作(Terminal Operations)
非短路操作:处理完所有数据才能得到结果,如
collect()、count()、forEach()、forEachOrdered()、max()、min()、reduce()、toArray()等;
短路(short-circuiting)操作:拿到符合预期的结果就会停下来,不一定会处理完所有数据。如anyMatch()、allMatch()、noneMatch()、findFirst()、findAny() 等;
Parallel Streams
streams可以是串行或者并行的;
在串行streams上的操作是在一个单线程中完成的,而并行streams上的操作是在多个线程上并发完成的;
使用并行stream可以充分利用多核处理器的优势,提升速度和性能,而所需要做的仅仅是将stream() 改为 parallelStream()即可;
为什么要使用Stream?
在命令式编程中,我们必须逐行编写代码,才能完成相关的计算,例如,计算1~10000的数的总和,我们需要使用for(int i=1;i<=10000;i++){….}循环语句来迭代求值,在这个语句中,我们需要花费额外的精力来维护loop变量的值,而在声明式编程中,我们只需要关注想要实现的目标,而不是其内部重复的循环逻辑;