JDK8~17 新特性(上)
Java 版本迭代概述
发布特点(小步快跑,快速迭代)
| 发行版本 | 发行时间 | 备注 |
|---|---|---|
| Java 1.0 | 1996.01.23 | Sun 公司发布了 Java 的第一个开发工具包 |
| Java 5.0 | 2004.09.30 | ①版本号从 1.4 直接更新至 5.0;②平台更名为 JavaSE、JavaEE、JavaME |
| Java 8.0 | 2014.03.18 | 此版本是继 Java 5.0 以来变化最大的版本。是长期支持版本(LTS) |
| Java 9.0 | 2017.09.22 | 此版本开始,每半年更新一次 |
| Java 10.0 | 2018.03.21 | |
| Java 11.0 | 2018.09.25 | JDK 安装包取消独立 JRE 安装包,是长期支持版本(LTS) |
| Java 12.0 | 2019.03.19 | |
| ... | ... | |
| Java17.0 | 2021.09 | 发布 Java 17.0,版本号也称为 21.9,是长期支持版本(LTS) |
| ... | ... | |
| Java19.0 | 2022.09 | 发布 Java19.0,版本号也称为 22.9。 |
从 Java 9 这个版本开始,Java 的计划发布周期是 6 个月。
这意味着 Java 的更新从传统的以特性驱动的发布周期,转变为以时间驱动的发布模式,并且承诺不会跳票。 通过这样的方式,开发团队可以把一些关键特性尽早合并到 JDK 之中, 以快速得到开发者反馈,在一定程度上避免出现像 Java 9 两次被迫延迟发布的窘况。
针对企业客户的需求,Oracle 将以三年为周期发布长期支持版本(long term support)。
Oracle 的官方观点认为,与 Java 7->8->9 相比,Java 9->10->11 的升级和 8->8u20->8u40 更相似。
新模式下的 Java 版本发布都会包含许多变更,包括语言变更和 JVM 变更,这两者都会对 IDE、字节码库和框架产生重大影响。 此外,不仅会新增其他 API,还会有 API 被删除(这在 Java 8 之前没有发生过)。
目前看这种发布策略是非常成功的,解开了 Java/JVM 演进的许多枷锁,至关重要的是,OpenJDK 的权力中心, 正在转移到开发社区和开发者手中。在新的模式中,既可以利用 LTS 满足企业长期可靠支持的需求, 也可以满足各种开发者对于新特性迭代的诉求。因为用 2-3 年的最小间隔粒度来试验一个特性,基本是不现实的。
名词解释
Oracle JDK 和 Open JDK
这两个 JDK 最大不同就是许可证不一样。但是对于个人用户来讲,没区别。
| Oracle JDK | Open JDK | |
|---|---|---|
| 来源 | Oracle 团队维护 | Oracle 和 Open Java 社区 |
| 授权协议 | Java 17 及更高版本 Oracle Java SE 许可证 Java16 及更低版本甲骨文免费条款和条件(NFTC)许可协议 | GPL v2 许可证 |
| 关系 | 由 Open JDK 构建,增加了少许内容 | |
| 是否收费 | 2021 年 9 月起 Java17 及更高版本所有用户免费。16 及更低版本,个人用户、开发用户免费。 | 2017 年 9 月起,所有版本免费 |
| 对语法的支持 | 一致 | 一致 |
JEP
JEP(JDK Enhancement Proposals),jdk 改进提案,每当需要有新的设想时候, JEP 可以提出非正式的规范 (specification),被正式认可的 JEP 正式写进 JDK 的发展路线图并分配版本号。
LTS
LTS(Long-term Support)即长期支持。Oracle 官网提供了对 Oracle JDK 个别版本的长期支持, 即使发发行了新版本,比如目前最新的 JDK19,在结束日期前,LTS 版本都会被长期支持。 (出了 bug,会被修复,非 LTS 则不会再有补丁发布)所以,一定要选一个 LTS 版本,不然出了漏洞没人修复了。
| 版本 | 开始日期 | 结束日期 | 延期结束日期 |
|---|---|---|---|
| 7(LTS) | 2011 年 7 月 | 2019 年 7 月 | 2022 年 7 月 |
| 8(LTS) | 2014 年 3 月 | 2022 年 3 月 | 2030 年 12 月 |
| 11(LTS) | 2018 年 9 月 | 2023 年 9 月 | 2026 年 9 月 |
| 17(LTS) | 2021 年 9 月 | 2026 年 9 月 | 2029 年 9 月 |
| 21(LTS) | 2023 年 9 月 | 2028 年 9 月 | 2031 年 9 月 |
如果要选择 Oracle JDK,目前可选的 LTS 版本为 8、11、17 三个。
各版本支持时间路线图

各版本介绍

jdk 9
Java 9 提供了超过 150 项新功能特性,包括备受期待的模块化系统、可交互的 REPL 工具 jshell,JDK 编译工具, Java 公共 API 和私有代码,以及安全增强、扩展提升、性能管理改善等。
特性太多,查看链接:
https://openjdk.java.net/projects/jdk9/
jdk 10
https://openjdk.java.net/projects/jdk/10/
286: Local-Variable Type Inference 局部变量类型推断
296: Consolidate the JDK Forest into a Single Repository JDK 库的合并
304: Garbage-Collector Interface 统一的垃圾回收接口
307: Parallel Full GC for G1 为 G1 提供并行的 Full GC
310: Application Class-Data Sharing 应用程序类数据(AppCDS)共享
312: Thread-Local Handshakes ThreadLocal 握手交互
313: Remove the Native-Header Generation Tool (javah) 移除 JDK 中附带的 javah 工具
314: Additional Unicode Language-Tag Extensions 使用附加的 Unicode 语言标记扩展
316: Heap Allocation on Alternative Memory Devices 能将堆内存占用分配给用户指定的备用内存设备
317: Experimental Java-Based JIT Compiler 使用 Graal 基于 Java 的编译器
319: Root Certificates 根证书
322: Time-Based Release Versioning 基于时间定于的发布版本
jdk 11
https://openjdk.java.net/projects/jdk/11/
181: Nest-Based Access Control 基于嵌套的访问控制
309: Dynamic Class-File Constants 动态类文件常量
315: Improve Aarch64 Intrinsics 改进 Aarch64 Intrinsics
318: Epsilon: A No-Op Garbage Collector Epsilon — 一个 No-Op(无操作)的垃圾收集器
320: Remove the Java EE and CORBA Modules 删除 Java EE 和 CORBA 模块
321: HTTP Client (Standard) HTTPClient API
323: Local-Variable Syntax for Lambda Parameters 用于 Lambda 参数的局部变量语法
324: Key Agreement with Curve25519 and Curve448 Curve25519 和 Curve448 算法的密钥协议
327: Unicode 10
328: Flight Recorder 飞行记录仪
329: ChaCha20 and Poly1305 Cryptographic Algorithms ChaCha20 和 Poly1305 加密算法
330: Launch Single-File Source-Code Programs 启动单一文件的源代码程序
331: Low-Overhead Heap Profiling 低开销的 Heap Profiling
332: Transport Layer Security (TLS) 1.3 支持 TLS 1.3
333: ZGC: A Scalable Low-Latency Garbage Collector (Experimental) 可伸缩低延迟垃圾收集器
335: Deprecate the Nashorn JavaScript Engine 弃用 Nashorn JavaScript 引擎
336: Deprecate the Pack200 Tools and API 弃用 Pack200 工具和 API
jdk 12
https://openjdk.java.net/projects/jdk/12/
189:Shenandoah: A Low-Pause-Time Garbage Collector (Experimental) 低暂停时间的 GC
230: Microbenchmark Suite 微基准测试套件
325: Switch Expressions (Preview) switch 表达式
334: JVM Constants API JVM 常量 API
340: One AArch64 Port, Not Two 只保留一个 AArch64 实现
341: Default CDS Archives 默认类数据共享归档文件
344: Abortable Mixed Collections for G1 可中止的 G1 Mixed GC
346: Promptly Return Unused Committed Memory from G1 G1 及时返回未使用的已分配内存
jdk 13
https://openjdk.java.net/projects/jdk/13/
350: Dynamic CDS Archives 动态 CDS 档案
351: ZGC: Uncommit Unused Memory ZGC:取消使用未使用的内存
353: Reimplement the Legacy Socket API 重新实现旧版套接字 API
354: Switch Expressions (Preview) switch 表达式(预览)
355: Text Blocks (Preview) 文本块(预览)
jdk 14
https://openjdk.java.net/projects/jdk/14/
305: Pattern Matching for instanceof (Preview) instanceof 的模式匹配
343: Packaging Tool (Incubator) 打包工具
345: NUMA-Aware Memory Allocation for G1 G1 的 NUMA-Aware 内存分配
349: JFR Event Streaming JFR 事件流
352: Non-Volatile Mapped Byte Buffers 非易失性映射字节缓冲区
358: Helpful NullPointerExceptions 实用的 NullPointerExceptions
359: Records (Preview)
361: Switch Expressions (Standard) Switch 表达式
362: Deprecate the Solaris and SPARC Ports 弃用 Solaris 和 SPARC 端口
363: Remove the Concurrent Mark Sweep (CMS) Garbage Collector 删除并发标记扫描(CMS)垃圾回收器
364: ZGC on macOS
365: ZGC on Windows
366: Deprecate the ParallelScavenge + SerialOld GC Combination 弃用 ParallelScavenge + SerialOld GC 组合
367: Remove the Pack200 Tools and API 删除 Pack200 工具和 API
368: Text Blocks (Second Preview) 文本块
370: Foreign-Memory Access API (Incubator) 外部存储器访问 API
jdk 15
https://openjdk.java.net/projects/jdk/15/
339: Edwards-Curve Digital Signature Algorithm (EdDSA) EdDSA 数字签名算法
360: Sealed Classes (Preview) 密封类(预览)
371: Hidden Classes 隐藏类
372: Remove the Nashorn JavaScript Engine 移除 Nashorn JavaScript 引擎
373: Reimplement the Legacy DatagramSocket API 重新实现 Legacy DatagramSocket API
374: Disable and Deprecate Biased Locking 禁用偏向锁定
375: Pattern Matching for instanceof (Second Preview) instanceof 模式匹配(第二次预览)
377: ZGC: A Scalable Low-Latency Garbage Collector ZGC:一个可扩展的低延迟垃圾收集器
378: Text Blocks 文本块
379: Shenandoah: A Low-Pause-Time Garbage Collector Shenandoah:低暂停时间垃圾收集器
381: Remove the Solaris and SPARC Ports 移除 Solaris 和 SPARC 端口
383: Foreign-Memory Access API (Second Incubator) 外部存储器访问 API(第二次孵化版)
384: Records (Second Preview) Records(第二次预览)
385: Deprecate RMI Activation for Removal 废弃 RMI 激活机制
jdk 16
https://openjdk.java.net/projects/jdk/16/
338: Vector API (Incubator) Vector API(孵化器)
347: Enable C++14 Language Features JDK C++ 的源码中允许使用 C++14 的语言特性
357: Migrate from Mercurial to Git OpenJDK 源码的版本控制从 Mercurial (hg) 迁移到 git
369: Migrate to GitHub OpenJDK 源码的版本控制迁移到 github 上
376: ZGC: Concurrent Thread-Stack Processing ZGC:并发线程处理
380: Unix-Domain Socket Channels Unix 域套接字通道
386: Alpine Linux Port 将 glibc 的 jdk 移植到使用 musl 的 alpine linux 上
387: Elastic Metaspace 弹性元空间
388: Windows/AArch64 Port 移植 JDK 到 Windows/AArch64
389: Foreign Linker API (Incubator) 提供 jdk.incubator.foreign 来简化 native code 的调用
390: Warnings for Value-Based Classes 提供基于值的类的警告
392: Packaging Tool jpackage 打包工具转正
393: Foreign-Memory Access API (Third Incubator)
394: Pattern Matching for instanceof Instanceof 的模式匹配转正
395: Records Records 转正
396: Strongly Encapsulate JDK Internals by Default 默认情况下,封装了 JDK 内部构件
397: Sealed Classes (Second Preview) 密封类
jdk 17
https://openjdk.java.net/projects/jdk/17/
306: Restore Always-Strict Floating-Point Semantics 恢复始终严格的浮点语义
356: Enhanced Pseudo-Random Number Generators 增强型伪随机数生成器
382: New macOS Rendering Pipeline 新的 macOS 渲染管道
391: macOS/AArch64 Port macOS/AArch64端口
398: Deprecate the Applet API for Removal 弃用 Applet API 后续将进行删除
403: Strongly Encapsulate JDK Internals 强封装 JDK 的内部 API
406: Pattern Matching for switch (Preview) switch 模式匹配(预览)
407: Remove RMI Activation 删除 RMI 激活机制
409: Sealed Classes 密封类转正
410: Remove the Experimental AOT and JIT Compiler 删除实验性的 AOT 和 JIT 编译器
411: Deprecate the Security Manager for Removal 弃用即将删除的安全管理器
412: Foreign Function & Memory API (Incubator) 外部函数和内存 API(孵化特性)
414: Vector API (Second Incubator) Vector API(第二次孵化特性)
415: Context-Specific Deserialization Filters 上下文特定的反序列化过滤器
JDK 各版本下载链接
https://www.oracle.com/java/technologies/downloads/archive/

如何学习新特性
对于新特性,我们应该从哪几个角度学习新特性呢?
语法层面
比如 JDK5 中的自动拆箱、自动装箱、enum、泛型,
比如 JDK8 中的 lambda 表达式、接口中的默认方法、静态方法,
比如 JDK10 中局部变量的类型推断,
比如 JDK12 中的 switch,
比如 JDK13 中的文本块。
API 层面
比如 JDK8 中的 Stream、Optional、新的日期时间、HashMap 的底层结构,
比如 JDK9 中 String 的底层结构,
新的 / 过时的 API。
底层优化
比如 JDK8 中永久代被元空间替代、新的 JS 执行引擎,
比如新的垃圾回收器、GC 参数、JVM 的优化。
Java8 新特性,Lambda 表达式
关于 Java8 新特性简介
Java 8 (又称为 JDK 8 或 JDK1.8) 是 Java 语言开发的一个主要版本。Java 8 是 oracle 公司于 2014 年 3 月发布, 可以看成是自 Java 5 以来最具革命性的版本。Java 8 为 Java 语言、编译器、类库、开发工具与 JVM 带来了大量新特性。
- 速度更快
- 代码更少 (增加了新的语法 Lambda 表达式)
- 强大的 Stream API
- 便于并行
- 并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。 相比较串行的流,并行的流可以很大程度上提高程序的执行效率
- Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。 Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与顺序流之间进行切换
- 最大化减少空指针异常:Optional
- Nashorn 引擎,允许在 JVM 上运行 JS 应用
- 发音“nass-horn”,是德国二战时一个坦克的命名
- javascript 运行在 jvm 已经不是新鲜事了,Rhino 早在 jdk6 的时候已经存在。现在替代 Rhino, 官方的解释是 Rhino 相比其他 JavaScript 引擎(比如 google 的 V8)实在太慢了, 改造 Rhino 还不如重写。所以 Nashorn 的性能也是其一个亮点
- Nashorn 项目在 JDK 9 中得到改进;在 JDK11 中 Deprecated,后续 JDK15 版本中 remove。 在 JDK11 中取以代之的是 GraalVM。(GraalVM 是一个运行时平台,它支持 Java 和其他基于 Java 字节码的语言, 但也支持其他语言,如 JavaScript,Ruby,Python 或 LLVM。性能是 Nashorn 的 2 倍以上。)
冗余的匿名内部类
当需要启动一个线程去完成任务时, 通常会通过 java.lang.Runnable 接口来定义任务内容,并使用 java.lang.Thread 类来启动该线程。代码如下:
package javacode.new_feature;
public class UseFunctionalProgramming {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("多线程任务执行!");
}
}).start(); // 启动线程
}
}本着“一切皆对象”的思想,这种做法是无可厚非的。首先创建一个 Runnable 接口的匿名内部类对象来指定任务内容, 再将其交给一个线程来启动。
代码分析
对于 Runnable 的匿名内部类用法,可以分析出几点内容:
- Thread 类需要 Runnable 接口作为参数,其中的抽象 run 方法是用来指定线程任务内容的核心
- 为了指定 run 的方法体,不得不需要 Runnable 接口的实现类
- 为了省去定义一个 RunnableImpl 实现类的麻烦,不得不使用匿名内部类
- 必须覆盖重写抽象 run 方法,这样也就导致了方法名称、方法参数、方法返回值又写了一遍
- 实际上只有方法体才是关键所在
Lambda 及其使用举例
Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。 使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使 Java 的语言表达能力得到了提升。
从匿名类到 Lambda 的转换举例 1

从匿名类到 Lambda 的转换举例 2

语法
在 Java 8 语言中引入的一种新的语法元素和操作符。这个操作符为 -> ,该操作符被称为 Lambda 操作符或箭头操作符。它将 Lambda 分为两个部分:
- 左侧指定了 Lambda 表达式需要的参数列表
- 右侧指定了 Lambda 体,是抽象方法的实现逻辑,也即 Lambda 表达式要执行的功能
格式一,无参,无返回值
@Test
public void test1() {
// 未使用 lambda 表达式
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("我爱北京天安门");
}
};
r1.run();
System.out.println("******************");
// 使用 lambda 表达式
Runnable r2 = () -> {
System.out.println("我爱北京故宫");
};
r2.run();
}格式二,Lambda 需要一个参数,但是没有返回值
@Test
public void test2() {
// 未使用 lambda 表达式
Consumer<String> con = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
con.accept("谎言和誓言的区别是什么?");
System.out.println("******************");
// 使用 lambda 表达式
Consumer<String> con1 = (String s) -> {
System.out.println(s);
};
con1.accept("一个是听的人当真了,一个是说的人当真了");
}格式三,数据类型可以省略,因为可由编译器推断得出,称为类型推断
@Test
public void test3() {
//
Consumer<String> con1 = (String s) -> {
System.out.println(s);
};
con1.accept("一个是听的人当真了,一个是说的人当真了");
System.out.println("******************");
// 省略类型
Consumer<String> con2 = s -> {
System.out.println(s);
};
con2.accept("一个是听的人当真了,一个是说的人当真了");
}格式四,Lambda 若只需要一个参数时,参数的小括号可以省略
@Test
public void test4() {
//
Consumer<String> con1 = (s) -> {
System.out.println(s);
};
con1.accept("一个是听得人当真了,一个是说的人当真了");
System.out.println("******************");
//
Consumer<String> con2 = s -> {
System.out.println(s);
};
con2.accept("一个是听得人当真了,一个是说的人当真了");
}格式五,Lambda 可以有两个或以上的参数,多条执行语句,并且可以有返回值
@Test
public void test5() {
// 未使用 lambda 表达式
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
}
};
System.out.println(com1.compare(12,21));
System.out.println("******************");
// 使用 lambda 表达式
Comparator<Integer> com2 = (o1,o2) -> {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
System.out.println(com2.compare(12,6));
}格式六,当 Lambda 体只有一条语句时,return 与大括号若有,都可以省略
@Test
public void test6() {
//语法格式六使用前
Comparator<Integer> com1 = (o1,o2) -> {
return o1.compareTo(o2);
};
System.out.println(com1.compare(12,6));
System.out.println("*****************************");
//语法格式六使用后
Comparator<Integer> com2 = (o1,o2) -> o1.compareTo(o2);
System.out.println(com2.compare(12,21));
}关于类型推断
在格式三中,Lambda 表达式中的参数类型都是由编译器推断得出的。
Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。 Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的“类型推断”。

Java8 新特性,函数式 (Functional) 接口
什么是函数式接口
- 只包含一个抽象方法(Single Abstract Method,简称 SAM)的接口,称为函数式接口。该接口可以包含其他非抽象方法
- 你可以通过 Lambda 表达式来创建该接口的对象。 (若 Lambda 表达式抛出一个受检异常 (即:非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明)
- 我们可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口。 同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口
- 在 java.util.function 包下定义了 Java 8 的丰富的函数式接口
如何理解函数式接口
简单的说,在 Java8 中,Lambda 表达式就是一个函数式接口的实例。这就是 Lambda 表达式和函数式接口的关系。 也就是说,只要一个对象是函数式接口的实例,那么该对象就可以用 Lambda 表达式来表示。
举例 1:

举例 2:
接口其实也可以看作是一个类。

作为参数传递 Lambda 表达式:

作为参数传递 Lambda 表达式:为了将 Lambda 表达式作为参数传递,接收 Lambda 表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口的类型。
Java 内置函数式接口
之前的函数式接口
之前学过的接口,有些就是函数式接口,比如:
- java.lang.Runnable
- public void run()
- java.lang.Iterable
- public Iterator iterate()
- java.lang.Comparable
- public int compareTo(T t)
- java.util.Comparator
- public int compare(T t1, T t2)
四大核心函数式接口
| 函数式接口 | 称谓 | 参数类型 | 用途 |
|---|---|---|---|
Consumer<T> | 消费型接口 | T | 对类型为 T 的对象应用操作,包含方法: void accept(T t) |
Supplier<T> | 供给型接口 | 无 | 返回类型为 T 的对象,包含方法:T get() |
Function<T, R> | 函数型接口 | T | 对类型为 T 的对象应用操作,并返回结果。结果是 R 类型的对象。包含方法:R apply(T t) |
Predicate<T> | 判断型接口 | T | 确定类型为 T 的对象是否满足某约束,并返回 boolean 值。包含方法:boolean test(T t) |
其它接口
类型 1:消费型接口
消费型接口的抽象方法特点:有形参,但是返回值类型是 void。
| 接口名 | 抽象方法 | 描述 |
|---|---|---|
BiConsumer<T,U> | void accept(T t, U u) | 接收两个对象用于完成功能 |
| DoubleConsumer | void accept(double value) | 接收一个 double 值 |
| IntConsumer | void accept(int value) | 接收一个 int 值 |
| LongConsumer | void accept(long value) | 接收一个 long 值 |
ObjDoubleConsumer<T> | void accept(T t, double value) | 接收一个对象和一个 double 值 |
ObjIntConsumer<T> | void accept(T t, int value) | 接收一个对象和一个 int 值 |
ObjLongConsumer<T> | void accept(T t, long value) | 接收一个对象和一个 long 值 |
类型 2:供给型接口
这类接口的抽象方法特点:无参,但是有返回值。
| 接口名 | 抽象方法 | 描述 |
|---|---|---|
| BooleanSupplier | boolean getAsBoolean() | 返回一个 boolean 值 |
| DoubleSupplier | double getAsDouble() | 返回一个 double 值 |
| IntSupplier | int getAsInt() | 返回一个 int 值 |
| LongSupplier | long getAsLong() | 返回一个 long 值 |
类型 3:函数型接口
这类接口的抽象方法特点:既有参数又有返回值。
| 接口名 | 抽象方法 | 描述 |
|---|---|---|
UnaryOperator<T> | T apply(T t) | 接收一个 T 类型对象,返回一个 T 类型对象结果 |
DoubleFunction<R> | R apply(double value) | 接收一个 double 值,返回一个 R 类型对象 |
IntFunction<R> | R apply(int value) | 接收一个 int 值,返回一个 R 类型对象 |
LongFunction<R> | R apply(long value) | 接收一个 long 值,返回一个 R 类型对象 |
ToDoubleFunction<T> | double applyAsDouble(T value) | 接收一个 T 类型对象,返回一个 double |
ToIntFunction<T> | int applyAsInt(T value) | 接收一个 T 类型对象,返回一个 int |
ToLongFunction<T> | long applyAsLong(T value) | 接收一个 T 类型对象,返回一个 long |
| DoubleToIntFunction | int applyAsInt(double value) | 接收一个 double 值,返回一个 int 结果 |
| DoubleToLongFunction | long applyAsLong(double value) | 接收一个 double 值,返回一个 long 结果 |
| IntToDoubleFunction | double applyAsDouble(int value) | 接收一个 int 值,返回一个 double 结果 |
| IntToLongFunction | long applyAsLong(int value) | 接收一个 int 值,返回一个 long 结果 |
| LongToDoubleFunction | double applyAsDouble(long value) | 接收一个 long 值,返回一个 double 结果 |
| LongToIntFunction | int applyAsInt(long value) | 接收一个 long 值,返回一个 int 结果 |
| DoubleUnaryOperator | double applyAsDouble(double operand) | 接收一个 double 值,返回一个 double |
| IntUnaryOperator | int applyAsInt(int operand) | 接收一个 int 值,返回一个 int 结果 |
| LongUnaryOperator | long applyAsLong(long operand) | 接收一个 long 值,返回一个 long 结果 |
BiFunction<T,U,R> | R apply(T t, U u) | 接收一个 T 类型和一个 U 类型对象,返回一个 R 类型对象结果 |
BinaryOperator<T> | T apply(T t, T u) | 接收两个 T 类型对象,返回一个 T 类型对象结果 |
ToDoubleBiFunction<T,U> | double applyAsDouble(T t, U u) | 接收一个 T 类型和一个 U 类型对象,返回一个 double |
ToIntBiFunction<T,U> | int applyAsInt(T t, U u) | 接收一个 T 类型和一个 U 类型对象,返回一个 int |
ToLongBiFunction<T,U> | long applyAsLong(T t, U u) | 接收一个 T 类型和一个 U 类型对象,返回一个 long |
| DoubleBinaryOperator | double applyAsDouble(double left, double right) | 接收两个 double 值,返回一个 double 结果 |
| IntBinaryOperator | int applyAsInt(int left, int right) | 接收两个 int 值,返回一个 int 结果 |
| LongBinaryOperator | long applyAsLong(long left, long right) | 接收两个 long 值,返回一个 long 结果 |
类型 4:判断型接口
这类接口的抽象方法特点:有参,但是返回值类型是 boolean 结果。
| 接口名 | 抽象方法 | 描述 |
|---|---|---|
BiPredicate<T,U> | boolean test(T t, U u) | 接收两个对象 |
| DoublePredicate | boolean test(double value) | 接收一个 double 值 |
| IntPredicate | boolean test(int value) | 接收一个 int 值 |
| LongPredicate | boolean test(long value) | 接收一个 long 值 |
内置接口代码演示
举例 1:
Consumer 接口的含义:

package javacode.new_feature;
import java.util.Arrays;
import java.util.List;
public class FuncTest1 {
public static void main(String[] args) {
List<String> strings = Arrays.asList("java", "C", "python", "c++");
// 遍历 Collection 集合,并将传递给 action 参数的操作代码应用在每一个元素上
// forEach 要求传入的参数类型为
// java.util.function.Consumer<? super T> action
strings.forEach(s -> System.out.println(s));
}
}举例 2:
Supplier 接口的含义:

package javacode.new_feature;
import java.util.function.Supplier;
public class FuncTest2 {
public static void main(String[] args) {
Supplier<String> supplier = () -> "hello Java";
System.out.println(supplier.get());
}
}举例 3:
Predicate 接口的含义:

package javacode.new_feature;
import java.util.ArrayList;
public class FuncTest3 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
list.add("atguigu");
list.add("ok");
list.add("yes");
System.out.println("删除之前:");
// forEach 传入 Consumer
list.forEach(t-> System.out.println(t));
//用于删除集合中满足 filter 指定的条件判断的。
//删除包含 o 字母的元素
list.removeIf(s -> s.contains("o"));
System.out.println("删除包含 o 字母的元素之后:");
list.forEach(t-> System.out.println(t));
}
}举例 4:
Function 接口的含义:

package javacode.new_feature;
import java.util.function.Function;
public class FuncTest4 {
public static void main(String[] args) {
// 使用 Lambda 表达式实现 Function<T,R> 接口,可以实现将一个字符串首字母转为大写的功能。
Function<String,String> fun = s -> s.substring(0,1).toUpperCase() + s.substring(1);
System.out.println(fun.apply("hello"));
}
}练习
练习 1:无参无返回值形式
假如有自定义函数式接口 Call 如下:
public interface Call {
void shout();
}在测试类中声明一个如下方法:
public static void callSomething(Call call){
call.shout();
}在测试类的 main 方法中调用 callSomething 方法,并用 Lambda 表达式为形参 call 赋值,可以喊出任意你想说的话。
package javacode.new_feature;
public class TestLambda {
public static void main(String[] args) {
callSomething(()->System.out.println("回家吃饭"));
callSomething(()->System.out.println("我爱你"));
callSomething(()->System.out.println("滚蛋"));
callSomething(()->System.out.println("回来"));
}
public static void callSomething(Call call){
call.shout();
}
}
interface Call {
void shout();
}练习 2:消费型接口
代码示例 Consumer 接口。
在 JDK1.8 中 Collection 集合接口的父接口 Iterable 接口中增加了一个默认方法 public default void forEach(Consumer<? super T> action),用于遍历 Collection 集合的每个元素,执行“xxx 消费型”操作。
在 JDK1.8 中 Map 集合接口中增加了一个默认方法 public default void forEach(BiConsumer<? super K,? super V> action), 用于遍历 Map 集合的每对映射关系,执行“xxx 消费型”操作。
案例:
(1)创建一个 Collection 系列的集合,添加一些字符串,调用 forEach 方法遍历查看
(2)创建一个 Map 系列的集合,添加一些 (key,value) 键值对,调用 forEach 方法遍历查看
示例代码:
@Test
public void test1(){
List<String> list = Arrays.asList("hello","java","lambda","atguigu");
list.forEach(s -> System.out.println(s));
}
@Test
public void test2(){
HashMap<Integer,String> map = new HashMap<>();
map.put(1, "hello");
map.put(2, "java");
map.put(3, "lambda");
map.put(4, "atguigu");
map.forEach((k,v) -> System.out.println(k+"->"+v));
}练习 3:供给型接口
代码示例:Supplier 接口。
在 JDK1.8 中增加了 StreamAPI,java.util.stream.Stream 是一个数据流。这个类型有一个静态方法 public static <T> Stream<T> generate(Supplier<T> s),可以创建 Stream 的对象,而 这个创建的对象又包含一个 forEach 方法可以遍历流中的元素 public void forEach(Consumer<? super T> action)。
案例:
现在请调用 Stream 的 generate 方法,来产生一个流对象,并调用 Math.random() 方法来产生数据, 为 Supplier 函数式接口的形参赋值。最后调用 forEach 方法遍历流中的数据查看结果。
@Test
public void test2(){
Stream.generate(() -> Math.random()).forEach(num -> System.out.println(num));
}练习 4:功能型接口
代码示例 Function<T,R> 接口。
在 JDK1.8 时 Map 接口增加了很多方法,例如:
public default void replaceAll(BiFunction<? super K,? super V,? extends V> function)按照 function 指定的操作替换 map 中的 valuepublic default void forEach(BiConsumer<? super K,? super V> action)遍历 Map 集合的每对映射关系,执行“xxx 消费型”操作
案例:
(1)声明一个 Employee 员工类型,包含编号、姓名、薪资
(2)添加 n 个员工对象到一个 HashMap<Integer,Employee>集合中,其中员工编号为 key,员工对象为 value
(3)调用 Map 的 forEach 遍历集合
(4)调用 Map 的 replaceAll 方法,将其中薪资低于 10000 元的,薪资设置为 10000
(5)再次调用 Map 的 forEach 遍历集合查看结果
Employee 类:
package javacode.new_feature;
public class Employee {
private int id;
private String name;
private double salary;
public Employee (Integer id) {}
public Employee(int id, String name, double salary) {
super();
this.id = id;
this.name = name;
this.salary = salary;
}
public Employee(int i, String tom, int i1, int i2) {
super();
}
public Employee(Integer id, String name) {
}
public Employee() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", salary=" + salary + "]";
}
}测试类:
package javacode.new_feature;
import java.util.HashMap;
public class TestEmployee {
public static void main(String[] args) {
HashMap<Integer,Employee> map = new HashMap<>();
Employee e1 = new Employee(1, "张三", 8000);
Employee e2 = new Employee(2, "李四", 9000);
Employee e3 = new Employee(3, "王五", 10000);
Employee e4 = new Employee(4, "赵六", 11000);
Employee e5 = new Employee(5, "钱七", 12000);
map.put(e1.getId(), e1);
map.put(e2.getId(), e2);
map.put(e3.getId(), e3);
map.put(e4.getId(), e4);
map.put(e5.getId(), e5);
map.forEach((k,v) -> System.out.println(k+"="+v));
System.out.println();
map.replaceAll((k,v)->{
if(v.getSalary()<10000){
v.setSalary(10000);
}
return v;
});
map.forEach((k,v) -> System.out.println(k+"="+v));
}
}练习 5:判断型接口
代码示例:Predicate 接口
JDK1.8 时,Collecton 接口增加了一下方法,其中一个如下:
public default boolean removeIf(Predicate<? super E> filter) 用于删除集合中满足 filter 指定的条件判断的。
public default void forEach(Consumer<? super T> action) 遍历 Collection 集合的每个元素,执行“xxx 消费型”操作。
案例:
(1)添加一些字符串到一个 Collection 集合中
(2)调用 forEach 遍历集合
(3)调用 removeIf 方法,删除其中字符串的长度<5 的
(4)再次调用 forEach 遍历集合
import java.util.ArrayList;
public class TestLambda {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("java");
list.add("atguigu");
list.add("ok");
list.add("yes");
list.forEach(str->System.out.println(str));
System.out.println();
list.removeIf(str->str.length()<5);
list.forEach(str->System.out.println(str));
}
}练习 6:判断型接口
案例:
(1)声明一个 Employee 员工类型,包含编号、姓名、性别,年龄,薪资。
(2)声明一个 EmployeeSerice 员工管理类,包含一个 ArrayList 集合的属性 all,在 EmployeeSerice 的构造器中,创建一些员工对象,为 all 集合初始化。
(3)在 EmployeeSerice 员工管理类中,声明一个方法:ArrayList get(Predicate p),即将满足 p 指定的条件的员工,添加到一个新的 ArrayList 集合中返回。
(4)在测试类中创建 EmployeeSerice 员工管理类的对象,并调用 get 方法,分别获取:
所有员工对象 所有年龄超过 35 的员工 所有薪资高于 15000 的女员工 所有编号是偶数的员工 名字是“张三”的员工 年龄超过 25,薪资低于 10000 的男员工 示例代码:
Employee 类:
public class Employee{
private int id;
private String name;
private char gender;
private int age;
private double salary;
public Employee(int id, String name, char gender, int age, double salary) {
super();
this.id = id;
this.name = name;
this.gender = gender;
this.age = age;
this.salary = salary;
}
public Employee() {
super();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", gender=" + gender + ", age=" + age + ", salary=" + salary
+ "]";
}
}员工管理类:
class EmployeeService{
private ArrayList<Employee> all;
public EmployeeService(){
all = new ArrayList<Employee>();
all.add(new Employee(1, "张三", '男', 33, 8000));
all.add(new Employee(2, "翠花", '女', 23, 18000));
all.add(new Employee(3, "无能", '男', 46, 8000));
all.add(new Employee(4, "李四", '女', 23, 9000));
all.add(new Employee(5, "老王", '男', 23, 15000));
all.add(new Employee(6, "大嘴", '男', 23, 11000));
}
public ArrayList<Employee> get(Predicate<Employee> p){
ArrayList<Employee> result = new ArrayList<Employee>();
for (Employee emp : result) {
if(p.test(emp)){
result.add(emp);
}
}
return result;
}
}测试类:
public class TestLambda {
public static void main(String[] args) {
EmployeeService es = new EmployeeService();
es.get(e -> true).forEach(e->System.out.println(e));
System.out.println();
es.get(e -> e.getAge()>35).forEach(e->System.out.println(e));
System.out.println();
es.get(e -> e.getSalary()>15000 && e.getGender()=='女').forEach(e->System.out.println(e));
System.out.println();
es.get(e -> e.getId()%2==0).forEach(e->System.out.println(e));
System.out.println();
es.get(e -> "张三".equals(e.getName())).forEach(e->System.out.println(e));
System.out.println();
es.get(e -> e.getAge()>25 && e.getSalary()<10000 && e.getGender()=='男').forEach(e->System.out.println(e));
}
}Java8 新特性,方法引用与构造器引用
Lambda 表达式是可以简化函数式接口的变量或形参赋值的语法。而方法引用和构造器引用是为了简化 Lambda 表达式的。
方法引用
方法引用可以看做是 Lambda 表达式深层次的表达。换句话说,方法引用就是 Lambda 表达式, 也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是 Lambda 表达式的一个语法糖。
方法引用格式
- 格式:使用方法引用操作符“::”将类 (或对象) 与 方法名分隔开来。
- 两个冒号
:中间不能有空格,而且必须英文状态下半角输入
- 两个冒号
如下三种主要使用情况:
- 情况 1:对象 :: 实例方法名
- 情况 2:类 :: 静态方法名
- 情况 3:类 :: 实例方法名
方法引用使用前提
要求 1
Lambda 体只有一句语句,并且是通过调用一个对象的/类现有的方法来完成的。
例如 System.out 对象,调用 println() 方法来完成 Lambda 体。
Math 类,调用 random() 静态方法来完成 Lambda 体。
要求 2
针对情况 1:函数式接口中的抽象方法 a 在被重写时使用了某一个对象的方法 b。 如果方法 a 的形参列表、返回值类型与方法 b 的形参列表、返回值类型都相同,则我们可以使用方法 b 实现对方法 a 的重写、替换。
针对情况 2:函数式接口中的抽象方法 a 在被重写时使用了某一个类的静态方法 b。 如果方法 a 的形参列表、返回值类型与方法 b 的形参列表、返回值类型都相同,则我们可以使用方法 b 实现对方法 a 的重写、替换。
针对情况 3:函数式接口中的抽象方法 a 在被重写时使用了某一个对象的方法 b。如果方法 a 的返回值类型与方法 b 的返回值类型相同, 同时方法 a 的形参列表中有 n 个参数,方法 b 的形参列表有 n-1 个参数, 且方法 a 的第 1 个参数作为方法 b 的调用者,且方法 a 的后 n-1 参数与方法 b 的 n-1 参数匹配(类型相同或满足多态场景也可以)。
举个例
package javacode.new_feature;
import org.junit.Test;
import java.io.PrintStream;
import java.util.Comparator;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public class MethodRefTest {
// 情况一:对象 :: 实例方法
// Consumer 中的 void accept(T t)
// PrintStream 中的 void println(T t)
@Test
public void test1() {
Consumer<String> con1 = str -> System.out.println(str);
con1.accept("北京");
System.out.println("*******************");
PrintStream ps = System.out;
Consumer<String> con2 = ps::println;
con2.accept("beijing");
}
// Supplier 中的 T get()
// Employee 中的 String getName()
@Test
public void test2() {
Employee emp = new Employee(1001,"Tom",23,5600);
Supplier<String> sup1 = () -> emp.getName();
System.out.println(sup1.get());
System.out.println("*******************");
Supplier<String> sup2 = emp::getName;
System.out.println(sup2.get());
}
// 情况二:类 :: 静态方法
// Comparator 中的 int compare(T t1,T t2)
// Integer 中的 int compare(T t1,T t2)
@Test
public void test3() {
Comparator<Integer> com1 = (t1, t2) -> Integer.compare(t1,t2);
System.out.println(com1.compare(12,21));
System.out.println("*******************");
Comparator<Integer> com2 = Integer::compare;
System.out.println(com2.compare(12,3));
}
// Function 中的 R apply(T t)
// Math 中的 Long round(Double d)
@Test
public void test4() {
Function<Double,Long> func = new Function<Double, Long>() {
@Override
public Long apply(Double d) {
return Math.round(d);
}
};
System.out.println("*******************");
Function<Double,Long> func1 = d -> Math.round(d);
System.out.println(func1.apply(12.3));
System.out.println("*******************");
Function<Double,Long> func2 = Math::round;
System.out.println(func2.apply(12.6));
}
// 情况三:类 :: 实例方法 (有难度)
// Comparator 中的 int comapre(T t1,T t2)
// String 中的 int t1.compareTo(t2)
@Test
public void test5() {
Comparator<String> com1 = (s1, s2) -> s1.compareTo(s2);
System.out.println(com1.compare("abc","abd"));
System.out.println("*******************");
Comparator<String> com2 = String :: compareTo;
System.out.println(com2.compare("abd","abm"));
}
// BiPredicate 中的 boolean test(T t1, T t2);
//String 中的 boolean t1.equals(t2)
@Test
public void test6() {
BiPredicate<String,String> pre1 = (s1, s2) -> s1.equals(s2);
System.out.println(pre1.test("abc","abc"));
System.out.println("*******************");
BiPredicate<String,String> pre2 = String :: equals;
System.out.println(pre2.test("abc","abd"));
}
// Function 中的 R apply(T t)
// Employee 中的 String getName();
@Test
public void test7() {
Employee employee = new Employee(1001, "Jerry", 23, 6000);
Function<Employee,String> func1 = e -> e.getName();
System.out.println(func1.apply(employee));
System.out.println("*******************");
Function<Employee,String> func2 = Employee::getName;
System.out.println(func2.apply(employee));
}
}构造器引用
当 Lambda 表达式是创建一个对象,并且满足 Lambda 表达式形参,正好是给创建这个对象的构造器的实参列表,就可以使用构造器引用。
格式:类名 :: new
举例:
package javacode.new_feature;
import org.junit.Test;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
public class ConstructorRefTest {
// 构造器引用
// Supplier 中的 T get()
// Employee 的空参构造器:Employee()
@Test
public void test1(){
Supplier<Employee> sup = new Supplier<Employee>() {
@Override
public Employee get() {
return new Employee();
}
};
System.out.println("*******************");
Supplier<Employee> sup1 = () -> new Employee();
System.out.println(sup1.get());
System.out.println("*******************");
Supplier<Employee> sup2 = () -> new Employee();
System.out.println(sup2.get());
}
// Function 中的 R apply(T t)
@Test
public void test2(){
Function<Integer,Employee> func1 = id -> new Employee(id);
Employee employee = func1.apply(1001);
System.out.println(employee);
System.out.println("*******************");
Function<Integer,Employee> func2 = Employee :: new;
Employee employee1 = func2.apply(1002);
System.out.println(employee1);
}
// BiFunction 中的 R apply(T t,U u)
@Test
public void test3(){
BiFunction<Integer,String,Employee> func1 = (id, name) -> new Employee(id,name);
System.out.println(func1.apply(1001,"Tom"));
System.out.println("*******************");
BiFunction<Integer,String,Employee> func2 = Employee :: new;
System.out.println(func2.apply(1002,"Tom"));
}
}数组构造引用
当 Lambda 表达式是创建一个数组对象,并且满足 Lambda 表达式形参,正好是给创建这个数组对象的长度,就可以数组构造引用。
格式:数组类型名 :: new
举例:
package javacode.new_feature;
import java.util.Arrays;
import java.util.function.Function;
public class Test11 {
public static void main(String[] args) {
Function<Integer,String[]> func1 = length -> new String[length];
String[] arr1 = func1.apply(5);
System.out.println(Arrays.toString(arr1));
System.out.println("*******************");
Function<Integer,String[]> func2 = String[] :: new;
String[] arr2 = func2.apply(10);
System.out.println(Arrays.toString(arr2));
}
}Java8 新特性,强大的 Stream API
说明
Java8 中有两大最为重要的改变。第一个是 Lambda 表达式,另外一个则是 Stream API。
Stream API ( java.util.stream) 把真正的函数式编程风格引入到 Java 中。 这是目前为止对 Java 类库最好的补充,因为 Stream API 可以极大提供 Java 程序员的生产力,让程序员写出高效率、干净、简洁的代码。
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。
使用 Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。
也可以使用 Stream API 来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
为什么要使用 Stream API
实际开发中,项目中多数数据源都来自于 MySQL、Oracle 等。 但现在数据源可以更多了,有 MongDB,Radis 等,而这些 NoSQL 的数据就需要 Java 层面去处理。
什么是 Stream
Stream 是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
Stream 和 Collection 集合的区别:
Collection 是一种静态的内存数据结构,讲的是数据;而 Stream 是有关计算的,讲的是计算。 前者是主要面向内存,存储在内存中,后者主要是面向 CPU,通过 CPU 实现计算。
注意:
- Stream 自己不会存储元素
- Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream
- Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。即一旦执行终止操作,就执行中间操作链,并产生结果
- Stream一旦执行了终止操作,就不能再调用其它中间操作或终止操作了
Stream 的操作三个步骤
1 创建 Stream
一个数据源(如:集合、数组),获取一个流。
2 中间操作
每次处理都会返回一个持有结果的新 Stream,即中间操作的方法返回值仍然是 Stream 类型的对象。 因此中间操作可以是个操作链,可对数据源的数据进行n次处理,但是在终结操作前,并不会真正执行。
3 终止操作(终端操作)
终止操作的方法返回值类型就不再是 Stream 了,因此一旦执行终止操作,就结束整个 Stream 操作了。 一旦执行终止操作,就执行中间操作链,最终产生结果并结束 Stream。

创建 Stream 实例
方式一:通过集合
Java8 中的 Collection 接口被扩展,提供了两个获取流的方法:
- default Stream stream(): 返回一个顺序流
- default Stream parallelStream(): 返回一个并行流
@Test
public void test01(){
List<Integer> list = Arrays.asList(1,2,3,4,5);
//JDK1.8 中,Collection 系列集合增加了方法
Stream<Integer> stream = list.stream();
}方式二:通过数组
Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:
- static Stream stream(T[] array): 返回一个流
- public static IntStream stream(int[] array)
- public static LongStream stream(long[] array)
- public static DoubleStream stream(double[] array)
@Test
public void test02(){
String[] arr = {"hello","world"};
Stream<String> stream = Arrays.stream(arr);
}
@Test
public void test03(){
int[] arr = {1,2,3,4,5};
IntStream stream = Arrays.stream(arr);
}方式三:通过 Stream 的 of()
可以调用 Stream 类静态方法 of(), 通过显示值创建一个流。它可以接收任意数量的参数。
- public static Stream of(T... values): 返回一个流
@Test
public void test04(){
Stream<Integer> stream = Stream.of(1,2,3,4,5);
stream.forEach(System.out::println);
}方式四:创建无限流 (了解)
可以使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流。
- 迭代
public static Stream iterate(final T seed, final UnaryOperator f)
- 生成
public static Stream generate(Supplier s)
// 方式四:创建无限流
@Test
public void test05() {
// 迭代
// public static<T> Stream<T> iterate(final T seed, final
// UnaryOperator<T> f)
Stream<Integer> stream = Stream.iterate(0, x -> x + 2);
stream.limit(10).forEach(System.out::println);
// 生成
// public static<T> Stream<T> generate(Supplier<T> s)
Stream<Double> stream1 = Stream.generate(Math::random);
stream1.limit(10).forEach(System.out::println);
}一系列中间操作
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作, 否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。
1 筛选与切片
| 方 法 | 描 述 |
|---|---|
| filter(Predicatep) | 接收 Lambda,从流中排除某些元素 |
| distinct() | 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素 |
| limit(long maxSize) | 截断流,使其元素不超过给定数量 |
| skip(long n) | 跳过元素,返回一个扔掉了前 n 个元素的流。 若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补 |
2 映射
| 方法 | 描述 |
|---|---|
| map(Function f) | 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 |
| mapToDouble(ToDoubleFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。 |
| mapToInt(ToIntFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。 |
| mapToLong(ToLongFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。 |
| flatMap(Function f) | 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流 |
3 排序
| 方法 | 描述 |
|---|---|
| sorted() | 产生一个新流,其中按自然顺序排序 |
| sorted(Comparator com) | 产生一个新流,其中按比较器顺序排序 |
代码举例:
package javacode.stream_test;
import org.junit.Test;
import java.util.Arrays;
import java.util.stream.Stream;
public class StreamMiddleOperate {
@Test
public void test01(){
//1、创建 Stream
Stream<Integer> stream = Stream.of(1,2,3,4,5,6);
//2、加工处理
//过滤:filter(Predicate p)
//把里面的偶数拿出来
/*
* filter(Predicate p)
* Predicate 是函数式接口,抽象方法:boolean test(T t)
*/
stream = stream.filter(t -> t%2==0);
//3、终结操作:例如:遍历
stream.forEach(System.out::println);
}
@Test
public void test02(){
Stream.of(1,2,3,4,5,6)
.filter(t -> t%2==0)
.forEach(System.out::println);
}
@Test
public void test03(){
Stream.of(1,2,3,4,5,6,2,2,3,3,4,4,5)
.distinct()
.forEach(System.out::println);
}
@Test
public void test04(){
Stream.of(1,2,3,4,5,6,2,2,3,3,4,4,5)
.limit(3)
.forEach(System.out::println);
}
@Test
public void test05(){
Stream.of(1,2,2,3,3,4,4,5,2,3,4,5,6,7)
.distinct() //(1,2,3,4,5,6,7)
.filter(t -> t%2!=0) //(1,3,5,7)
.limit(3)
.forEach(System.out::println);
}
@Test
public void test06(){
Stream.of(1,2,3,4,5,6,2,2,3,3,4,4,5)
.skip(5)
.forEach(System.out::println);
}
@Test
public void test07(){
Stream.of(1,2,3,4,5,6,2,2,3,3,4,4,5)
.skip(5)
.distinct()
.filter(t -> t%3==0)
.forEach(System.out::println);
}
@Test
public void test08(){
long count = Stream.of(1,2,3,4,5,6,2,2,3,3,4,4,5)
.distinct()
.peek(System.out::println) //Consumer 接口的抽象方法 void accept(T t)
.count();
System.out.println("count="+count);
}
@Test
public void test09(){
//希望能够找出前三个最大值,前三名最大的,不重复
Stream.of(11,2,39,4,54,6,2,22,3,3,4,54,54)
.distinct()
.sorted((t1,t2) -> -Integer.compare(t1, t2))//Comparator 接口 int compare(T t1, T t2)
.limit(3)
.forEach(System.out::println);
}
@Test
public void test10(){
Stream.of(1,2,3,4,5)
.map(t -> t+=1)//Function<T,R>接口抽象方法 R apply(T t)
.forEach(System.out::println);
}
@Test
public void test11(){
String[] arr = {"hello","world","java"};
Arrays.stream(arr)
.map(t->t.toUpperCase())
.forEach(System.out::println);
}
@Test
public void test12(){
String[] arr = {"hello","world","java"};
Arrays.stream(arr)
.flatMap(t -> Stream.of(t.split("|")))//Function<T,R>接口抽象方法 R apply(T t) 现在的 R 是一个 Stream
.forEach(System.out::println);
}
}终止操作
终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如 List、Integer,甚至是 void。
流进行了终止操作后,不能再次使用。
1 匹配与查找
| 方法 | 描述 |
|---|---|
| allMatch(Predicate p) | 检查是否匹配所有元素 |
| **anyMatch(Predicate p) ** | 检查是否至少匹配一个元素 |
| noneMatch(Predicate p) | 检查是否没有匹配所有元素 |
| findFirst() | 返回第一个元素 |
| findAny() | 返回当前流中的任意元素 |
| count() | 返回流中元素总数 |
| max(Comparator c) | 返回流中最大值 |
| min(Comparator c) | 返回流中最小值 |
| forEach(Consumer c) | 内部迭代 (使用 Collection 接口需要用户去做迭代,称为外部迭代。 相反,Stream API 使用内部迭代——它帮你把迭代做了) |
2 归约
| 方法 | 描述 |
|---|---|
| reduce(T identity, BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回 T |
| reduce(BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回 Optional<T> |
TIP
map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它来进行网络搜索而出名。
3 收集
| 方 法 | 描 述 |
|---|---|
| collect(Collector c) | 将流转换为其他形式。接收一个 Collector 接口的实现, 用于给 Stream 中元素做汇总的方法 |
Collector 接口中方法的实现决定了如何对流执行收集的操作 (如收集到 List、Set、Map)。
另外,Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:
| 方法 | 返回类型 | 作用 |
|---|---|---|
| toList | Collector<T, ?, List<T>> | 把流中元素收集到 List |
List<Employee> emps= list.stream().collect(Collectors.toList());| 方法 | 返回类型 | 作用 |
|---|---|---|
| toSet | Collector<T, ?, Set<T>> | 把流中元素收集到 Set |
Set<Employee> emps= list.stream().collect(Collectors.toSet());| 方法 | 返回类型 | 作用 |
|---|---|---|
| toCollection | Collector<T, ?, C> | 把流中元素收集到创建的集合 |
Collection<Employee> emps =list.stream().collect(Collectors.toCollection(ArrayList::new));| 方法 | 返回类型 | 作用 |
|---|---|---|
| counting | Collector<T, ?, Long> | 计算流中元素的个数 |
long count = list.stream().collect(Collectors.counting());| 方法 | 返回类型 | 作用 |
|---|---|---|
| summingInt | Collector<T, ?, Integer> | 对流中元素的整数属性求和 |
int total=list.stream().collect(Collectors.summingInt(Employee::getSalary));| 方法 | 返回类型 | 作用 |
|---|---|---|
| averagingInt | Collector<T, ?, Double> | 计算流中元素 Integer 属性的平均值 |
double avg = list.stream().collect(Collectors.averagingInt(Employee::getSalary));| 方法 | 返回类型 | 作用 |
|---|---|---|
| summarizingInt | Collector<T, ?, IntSummaryStatistics> | 收集流中 Integer 属性的统计值。如:平均值 |
int SummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Employee::getSalary));| 方法 | 返回类型 | 作用 |
|---|---|---|
| joining | Collector<CharSequence, ?, String> | 连接流中每个字符串 |
String str= list.stream().map(Employee::getName).collect(Collectors.joining());| 方法 | 返回类型 | 作用 |
|---|---|---|
| maxBy | Collector<T, ?, Optional<T>> | 根据比较器选择最大值 |
Optional<Emp>max= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)));| 方法 | 返回类型 | 作用 |
|---|---|---|
| minBy | Collector<T, ?, Optional<T>> | 根据比较器选择最小值 |
Optional<Emp> min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary)));| 方法 | 返回类型 | 作用 |
|---|---|---|
| reducing | Collector<T, ?, Optional<T>> | 从一个作为累加器的初始值开始,利用 BinaryOperator 与流中元素逐个结合,从而归约成单个值 |
int total=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum));| 方法 | 返回类型 | 作用 |
|---|---|---|
| collectingAndThen | Collector<T,A,RR> | 包裹另一个收集器,对其结果转换函数 |
int how= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));| 方法 | 返回类型 | 作用 |
|---|---|---|
| groupingBy | Collector<T, ?, Map<K, List<T>>> | 根据某属性值对流分组,属性为 K,结果为 V |
Map<Emp.Status, List<Emp>> map= list.stream().collect(Collectors.groupingBy(Employee::getStatus));| 方法 | 返回类型 | 作用 |
|---|---|---|
| partitioningBy | Collector<T, ?, Map<Boolean, List<T>>> | 根据 true 或 false 进行分区 |
Map<Boolean,List<Emp>> vd = list.stream().collect(Collectors.partitioningBy(Employee::getManage));举例:
package javacode.stream_test;
import org.junit.Test;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamEndding {
@Test
public void test01(){
Stream.of(1,2,3,4,5)
.forEach(System.out::println);
}
@Test
public void test02(){
long count = Stream.of(1,2,3,4,5)
.count();
System.out.println("count = " + count);
}
@Test
public void test03(){
boolean result = Stream.of(1,3,5,7,9)
.allMatch(t -> t%2!=0);
System.out.println(result);
}
@Test
public void test04(){
boolean result = Stream.of(1,3,5,7,9)
.anyMatch(t -> t%2==0);
System.out.println(result);
}
@Test
public void test05(){
Optional<Integer> opt = Stream.of(1,3,5,7,9).findFirst();
System.out.println(opt);
}
@Test
public void test06(){
Optional<Integer> opt = Stream.of(1,2,3,4,5,7,9)
.filter(t -> t%3==0)
.findFirst();
System.out.println(opt);
}
@Test
public void test07(){
Optional<Integer> opt = Stream.of(1,2,4,5,7,8)
.filter(t -> t%3==0)
.findFirst();
System.out.println(opt);
}
@Test
public void test08(){
Optional<Integer> max = Stream.of(1,2,4,5,7,8)
.max((t1,t2) -> Integer.compare(t1, t2));
System.out.println(max);
}
@Test
public void test09(){
Integer reduce = Stream.of(1,2,4,5,7,8)
.reduce(0, (t1,t2) -> t1+t2);//BinaryOperator 接口 T apply(T t1, T t2)
System.out.println(reduce);
}
@Test
public void test10(){
Optional<Integer> max = Stream.of(1,2,4,5,7,8)
.reduce((t1,t2) -> t1>t2?t1:t2);//BinaryOperator 接口 T apply(T t1, T t2)
System.out.println(max);
}
@Test
public void test11(){
List<Integer> list = Stream.of(1,2,4,5,7,8)
.filter(t -> t%2==0)
.collect(Collectors.toList());
System.out.println(list);
}
}Java9 新增 API
新增 1,Stream 实例化方法
ofNullable() 的使用:
Java 8 中 Stream 不能完全为 null,否则会报空指针异常。 而 Java 9 中的 ofNullable 方法允许我们创建一个单元素 Stream,可以包含一个非空元素,也可以创建一个空 Stream。
//报 NullPointerException
//Stream<Object> stream1 = Stream.of(null);
//System.out.println(stream1.count());
//不报异常,允许通过
Stream<String> stringStream = Stream.of("AA", "BB", null);
System.out.println(stringStream.count());//3
//不报异常,允许通过
List<String> list = new ArrayList<>();
list.add("AA");
list.add(null);
System.out.println(list.stream().count());//2
//ofNullable():允许值为 null
Stream<Object> stream1 = Stream.ofNullable(null);
System.out.println(stream1.count());//0
Stream<String> stream = Stream.ofNullable("hello world");
System.out.println(stream.count());//1iterator() 重载的使用:
//原来的控制终止方式:
Stream.iterate(1,i -> i + 1).limit(10).forEach(System.out::println);
//现在的终止方式:
Stream.iterate(1,i -> i < 100,i -> i + 1).forEach(System.out::println);