一、Java版本变迁速览
- Java 1.0 (1996.01.23): Sun 公司发布第一个开发工具包。
- Java 5.0 (2004.09.30): 版本号直接更新至 5.0,平台更名为 Java SE、Java EE、Java ME。
- Java 8.0 (2014.03.18): 变化最大,长期支持版本(LTS)。
- Java 9.0 (2017.09.22): 开始每半年更新一次。
- Java 10.0 (2018.03.21)
- Java 11.0 (2018.09.25): 取消独立 JRE 安装包,长期支持版本(LTS)。
- Java 12.0 (2019.03.19)
- Java 17.0 (2021.09): 版本号称为 21.9,长期支持版本(LTS)。
- Java 19.0 (2022.09): 版本号称为 22.9。
从 Java 9 开始,Java 的发布周期为 6 个月,更新由特性驱动转变为时间驱动,承诺不再延迟。此举使得关键特性能快速合并到 JDK,开发团队能更早获得反馈。Oracle 计划每三年发布一个长期支持版本(LTS)。
📌LTS小贴士:
LTS(Long-term Support)(长期支持)版本在新版本发布后仍会获得支持和修复,如 JDK 19。选择 LTS 版本可以确保在出现漏洞时获得修复。
学习新特性的角度
语法层面:
- JDK 5: 自动拆箱、装箱、枚举、泛型。
- JDK 8: Lambda 表达式、接口默认方法、静态方法。
- JDK 10: 局部变量类型推断。
- JDK 12: 新的 switch 语法。
- JDK 13: 文本块。
API 层面:
- JDK 8: Stream、Optional、新的日期时间 API、HashMap 底层结构。
- JDK 9: String 的底层结构。
- 新增或过时的 API。
底层优化:
- JDK 8: 永久代被元空间替代、新的 JS 执行引擎。
- 新的垃圾回收器、GC 参数、JVM 优化。
二、改变编码方式的三大神器
1.函数式编程思想概述
函数式编程 (Functional Programming):在数学中,函数是输入和输出的计算方案,即“用数据进行操作”。与面向对象强调“通过对象做事情”不同,函数式编程关注“做什么,而非如何做”。学习 Lambda 表达式就是这一思想的体现。
1. Lambda表达式-Java8新特性
1.1 启动线程实例
- 方式1:普通方式
- 方式2:匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("多线程方式启动");
}
}).start();
- 方式3:使用Lambda表达式
new Thread(()->{
System.out.println("多线程方式启动");
}).start();
new Thread(()->System.out.println("多线程方式启动")).start();
1.2 Lambda表达式的标准格式
Lambda 表达式在 Java 8 中引入,使用“->”作为操作符,分为两部分:
- 左侧: 参数列表
- 右侧: Lambda 体,实现逻辑
组成要素:形式参数、箭头、代码块。
1.3 使用示例
- 语法格式一:无参、无返回值
//匿名内部类
useEatable(new Eatable() {
@Override
public void eat() {
System.out.println("一天一苹果,医生远离我!");
}
});
//Lambda表达式
useEatable(()->{
System.out.println("一天一苹果,医生远离我!");
});
- 语法格式二:一个参数,但无返回值
useFlyable(new Flyable() {
@Override
public void fly(String s) {
System.out.println(s);
System.out.println("飞机自驾游!");
}
});
useFlyable((String s)->{
System.out.println(s);
System.out.println("飞机自驾游!");
});
1.3 Lambda表达式的省略模式
省略规则:
- 类型推断: 参数类型可省略,编译器会推断。
useAddable((x, y)->{});
- 单参数: 若仅有一个参数,小括号可省略。
useFlyable(s->{});
- 单行代码: 语句只有一条时,可以省略大括号和分号,甚至是return。
useFlyable(s->System.out.println("飞机自驾游!"));
useAddable((x, y)->x + y);
1.4 注意事项
- Lambda 必须在接口中定义且只能有一个抽象方法。
- 需要上下文环境推导出对应接口。
Runnable r = () -> System.out.println("Lambda表达式");
new Thread(() -> System.out.println("Lambda表达式")).start();
1.5 Lambda表达式和匿名内部类的区别
1. 所需类型:
- 匿名内部类:接口、抽象类或具体类。
- Lambda 表达式:只能是接口。
2. 使用限制: - 单抽象方法接口可用 Lambda 和匿名内部类。
- 多于一个抽象方法的接口只能用匿名内部类。
3. 实现原理: - 匿名内部类:编译后生成单独的 .class 文件。
- Lambda 表达式:运行时动态生成字节码,无单独 .class 文件。
2. 函数式(Functional)接口
2.1 什么是函数式接口?
- 定义: 函数式接口是仅包含一个抽象方法(Single Abstract Method, SAM)的接口,可以包含其他非抽象方法。
- 使用: 通过 Lambda 表达式创建该接口的对象。如果 Lambda 表达式抛出受检异常,该异常需在接口的抽象方法中声明。
- 注解: 使用 @FunctionalInterface 注解可以验证接口是否为函数式接口,并在 javadoc 中说明。
- Java 8: java.util.function 包下定义了多种函数式接口。
简单的说,在Java8中,Lambda表达式就是一个函数式接口的实例。只有一个对象是函数式接口的实例,那么该对象就可以用Lambda表达式来表示。
2.2 函数式接口作为方法的参数
为了将 Lambda 表达式作为参数传递,接收参数的类型必须是兼容的函数式接口。
public class RunnableDemo {
public static void main(String[] args) {
startThread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"启动了");
}
});
startThread(()-> System.out.println(Thread.currentThread().getName()+"启动了"));
}
private static void startThread(Runnable r){
new Thread(r).start();
System.out.println(Thread.currentThread().getName()+"启动了");
}
}
2.3 函数式接口作为方法的返回值
需求: 定义一个类(ComparatorDemo),在类中提供两个方法
- Comparator getComparator() 返回一个函数式接口。
- 主方法调用 getComparator()。
如果方法的返回值是一个函数式接口,我们可以使用Lambda表达式作为结果返回
public class ComparatorDemo {
public static void main(String[] args) {
ArrayList<String> al = new ArrayList<>();
al.add("a");
al.add("dddddd");
al.add("bbb");
al.add("cc");
System.out.println("排序前:"+al);
Collections.sort(al, getComparator());
System.out.println("排序后:"+al);
}
private static Comparator<String> getComparator(){
/* Comparator<String> com = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length()-o2.length();
}
};
return com;*/
//return new Comparator<String>() {
// @Override
// public int compare(String o1, String o2) {
// return o1.length()-o2.length();
// }
//};
return (String s1, String s2)->{
return s1.length() - s2.length();
};
}
}
2.4 常用的函数式接口
Java 8 在 java.util.function 包中预定义了多种函数式接口。以下是一些常见的函数式接口:
java.lang.Runnable: void run()
java.lang.Iterable: Iterator<T> iterator()
java.lang.Comparable: int compareTo(T t)
java.util.Comparator: int compare(T t1, T t2)
重点学习以下四大核心函数式接口:
接口 | 参数类型 | 用途 |
---|---|---|
Consumer | T | 对类型为 T 的对象执行操作,包含方法 void accept(T t) |
Supplier | 无 | 生成类型为 T 的对象,包含方法 T get() |
Function<T, R> | T | 对类型为 T 的对象进行操作并返回 R 类型的结果,包含方法 R apply(T t) |
Predicate | T | 判断类型为 T 的对象是否满足某个条件,返回 boolean 值,包含方法 boolean test(T t) |
2.4.1 Supplier接口
Supplier 接口被称为生产型接口,提供一个无参的 get() 方法,返回指定类型的数据。
- Supplier:包含一个无参的方法
- T get():获得结果,该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据
public class SupplierDemo {
public static void main(String[] args) {
String s = getString(() -> "林青霞");
Integer i = getInteger(() -> 15);
System.out.println(s + ", " + i);
}
private static String getString(Supplier<String> sup) {
return sup.get();
}
private static Integer getInteger(Supplier<Integer> sup) {
return sup.get();
}
}
练习
定义 SupplierTest 类,提供 getMax(Supplier sup) 方法,返回 int 数组中的最大值。
public class SupplierTest {
public static void main(String[] args) {
int []arr = {19, 50, 28, 37, 46};
int maxValue = getMax(()->{
int max = arr[0];
for (int i = 1; i < arr.length; ++i){
max = arr[i] > max ? arr[i] : max;
}
return max;
});
System.out.println(maxValue);
}
private static int getMax(Supplier<Integer> sup){
return sup.get();
}
}
2.4.2 Consumer消费型接口
Consumer 接口用于消费数据,包含:
- void accept(T t): 对参数执行操作。
- default Consumer andThen(Consumer<? super T> after): 组合两个 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 消费型”操作。
public class ConsumerDemo {
public static void main(String[] args) {
operatorString("林青霞", (String s)->{
System.out.println(s);
});
operatorString("林青霞", s->System.out.println(s));
operatorString("林青霞", System.out::println);
System.out.println("================");
operatorString("林青霞", s->System.out.println(s),
s->System.out.println(new StringBuilder(s).reverse().toString()));
}
//消费一个字符串数据
private static void operatorString(String name, Consumer<String> con){
con.accept(name);
}
//用不同的方式消费一个字符串数据两次
private static void operatorString(String name, Consumer<String> con1, Consumer<String> con2){
//con1.accept(name);
//con2.accept(name);
con1.andThen(con2).accept(name);
}
练习
String[] strArray= {“林青霞,30”, “张曼玉,35”, “王祖贤,33”},字符串数组中有多条信息,请按照格式:“姓名: XX年龄: XX "的格式将信息打印出来
要求:
- 把打印姓名的动作作为第一个Consumer接 的Lambda实例
- 把打印年龄的动作作为第二个Consumer接口的Lambda实例
- 将两个Consumer接[按照顺序组合到一起使用
public class ConsumerTest {
public static void main(String[] args) {
String[] strArray = {"林青霞,30", "张曼玉,35", "王祖贤,33"};
printInfo(strArray, (String str)-> {
String name = str.split(",")[0];
System.out.print("姓名," + name);
},(String str)-> {
int age = Integer.parseInt(str.split(",")[1]);
System.out.println(" 年龄," + age);
});
System.out.println("======");
printInfo(strArray, str->System.out.print("姓名," + str.split(",")[0]),
str->System.out.println(" 年龄," + Integer.parseInt(str.split(",")[1])));
}
private static void printInfo(String[] strArray, Consumer<String>con1, Consumer<String>con2){
for (String str: strArray){
con1.andThen(con2).accept(str);
}
}
}
2.4.3 Predicate接口
Predicate 用于判断条件,包含:
- boolean test(T t):对给定的参数进行判断(判断逻辑由Lambda表达式实现), 返回一个布尔值
- default Predicate negate():返回一个逻辑的否定,对应逻辑非
- default Predicate and(Predicate other):返回一个组合判断,对应短路与
- default Predicate or(Predicate other):返回一个组合判断,对应
JDK1.8 时,Collecton 接口增加了一下方法,其中一个如下:
- public default boolean removeIf(Predicate<? super E> filter) 用于删除集合中满足 filter 指定的条件判断的。
public class PredicateDemo01 {
public static void main(String[] args) {
boolean b1 = checkString("hello", s->s.length()>8);
System.out.println(b1);
boolean b2 = checkString("helloWorld", s->s.length()>8);
System.out.println(b2);
boolean b3 = checkString("helloworld", s->s.length()<8, s->s.length()>15);
System.out.println(b3);
}
private static boolean checkString(String s, Predicate<String> pre){
//return pre.test(s);
//return !pre.test(s);
return pre.negate().test(s);
}
private static boolean checkString(String s, Predicate<String> pre1, Predicate<String> pre2){
//boolean b1 = pre1.test(s);
//boolean b2 = pre2.test(s);
//return b1 && b2;
//return pre1.and(pre2).test(s);//逻辑与判断
return pre1.or(pre2).test(s);
}
练习
筛选姓名长度大于2且年龄大于33的字符串。
public static void main(String[] args) {
String[] IstrArray= {"林青霞,30","柳岩,34", "张曼玉,35", "貂蝉,31","王祖贤,33"};
ArrayList<String> str = filter(IstrArray, s->s.split(",")[0].length()>2,
s->Integer.parseInt(s.split(",")[1])>33);
for (String s: str){
System.out.println(s.toString());
}
}
private static ArrayList<String> filter(String[] al, Predicate<String> pre1, Predicate<String> pre2){
ArrayList<String> s = new ArrayList<String>();
for (String str : al){
if (pre1.and(pre2).test(str)){
s.add(str);
}
}
return s;
}
2.4.4 Function接口
Function<T, R> 接口用于处理参数并返回新值,包含:
- R apply(T t): 处理输入并返回结果。
- default Function<T, V> andThen(Function<? super R, ? extends V> after): 返回组合函数。
在 JDK1.8 时 Map 接口增加了很多方法,例如:
- public default void replaceAll(BiFunction<? super K,? super V,? extends V> function) 按照 function 指定的操作替换 map 中的 value。
- public default void forEach(BiConsumer<? super K,? super V> action) 遍历 Map 集合的每对映射关系,执行“xxx 消费型”操作。
package function;
import java.util.function.Function;
public class FunctionDemo {
public static void main(String[] args) {
convert("100", (String s)->{
return Integer.parseInt(s);
});
convert("100", s->Integer.parseInt(s));
convert("100", Integer::new);
convert(100, i->String.valueOf(99+i));
convert("100", s->Integer.parseInt(s), i->String.valueOf(i+199));
}
//定义一个方法,把一个字符串转换int类型,在控制台输出
private static void convert(String s, Function<String, Integer> fun){
int i = fun.apply(s);
System.out.println(i);
}
//定义一个方法,把一个int类型的数据加上一个整数之后,转为字符串在控制台输出
private static void convert(int i, Function<Integer, String> fun){
String s = fun.apply(i);
System.out.println(s);
}
//定义一个方法,把一个字符串转换int类型,把int类型的数据加上一个整数之后,转为字符串在控制台输出
private static void convert(String s, Function<String, Integer> fun1, Function<Integer, String> fun2){
//Integer i = fun1.apply(s);
//String ss = fun2.apply(i);
// System.out.println(ss);
String ss = fun1.andThen(fun2).apply(s);
System.out.println(ss);
}
}
练习
通过 Function 接口实现函数拼接,将字符串处理为年龄并加70。
package function;
import java.util.function.Function;
public class FucntionTest {
public static void main(String[] args) {
String str = "林青霞,30";
convert(str, s->s.split(",")[1], s->Integer.parseInt(s), i->70+i);
}
private static void convert(String s, Function<String, String>fun1, Function<String, Integer>fun2, Function<Integer, Integer> fun3){
Integer i = fun1.andThen(fun2).andThen(fun3).apply(s);
System.out.println(i);
}
}
Stream API(Java8)
🔄 什么是Stream?
- 数据处理流水线:类似SQL的链式操作,支持过滤/映射等复杂计算
- 与集合的本质区别:
- 🗃️ 集合 = 静态数据存储(关注数据本身)
- 🚀 Stream = 动态数据计算(关注数据处理)
⚡ 为什么需要Stream?
实际开发中,项目中多数数据源都来自于 MySQL、Oracle 等。但现在数据源可以更多了,有 MongDB,Radis 等,而这些 NoSQL 的数据就需要 Java 层面去处理。
// 传统方式 vs Stream方式(代码量对比)
List<String> result = new ArrayList<>();
for(String s : list) {
if(s.startsWith("张") && s.length()==3) {
result.add(s);
}
}
list.stream()
.filter(s -> s.startsWith("张"))
.filter(s -> s.length() == 3)
.forEach(System.out::println);
📌 三大特性
- 无存储:不保存原始数据(仅操作通道)
- 无污染:原始数据永不修改(返回新Stream)
- 延迟执行:终结操作触发执行链
🛠️ 操作三步曲
- 生成流:通过数据源(集合,数组等)生成流 list.stream()
- 中间操作:一个流后面可以跟随零个或多个中间操作,其目的主要是打开流,做出某种程度的数据过滤/映射,然后返回一个新的流,交给下一个操作使用 filter()
- 终结操作:一个流只能有一个终结操作, 当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作,forEach()
🌐 流生成方式
数据源 | 生成方法 | 示例 |
---|---|---|
Collection集合 | .stream() | list.stream() |
数组 | Arrays.stream() | Arrays.stream(arr) |
散列数据 | 间接转换 | map.keySet().stream() |
直接创建 | Stream.of() | Stream.of(“a”,“b”,“c”) |
需求:按照下面的要求完成集合的创建和遍历
- 创建一个集合,存储多个字符串元素
- 把集合中所有以"张"开头的元素存储到一个新的集合
- 把"张"开头的集合中的长度为3的元素存储到一个新的集合
- 遍历上一 步得到的集合
使用Stream流的方式完成过滤操作
list.stream().filter(s -> s.startsWith("张 ).filter(s -> s.length() == 3).forEach(System.out:println); - 直接阅读代码的字面意思即可完美 展示无关逻辑方式的语义:生成流、过滤姓张、过滤长度为3、逐一打印
- Stream流把真正的函数式编程风格 入到Java中
package stream;
import java.util.*;
import java.util.stream.Stream;
public class StreamDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
Stream<String> listStream = list.stream();
Set<String> set = new HashSet<String>();
Stream<String> setStream = set.stream();
//Map 间接生成流
HashMap<String, Integer> map = new HashMap<>();
Stream<String> keyStream = map.keySet().stream();
Stream<Integer> valueStream = map.values().stream();
Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();
//数组
String[] strArray = {"hello", "world", "java"};
IntStream stream = Arrays.stream(strArray);
Stream<String> strArrayStream = Stream.of(strArray);
Stream<String> strArrayStream2 = Stream.of("hello", "world", "java");
Stream<Integer> integerStream = Stream.of(10, 20, 30);
//动态添加流
Stream.builder<String> streamBuilder = Stream.builder();
streamBuilder.add("a");
streamBuilder.add("b");
Stream<String> stream = streamBuilder.build();
}
}
常见中间操作方法
筛选与切片
- Stream filter(Predicate predicate):用于对流中的数据进行过滤
Predicate接口中的方法 boolean test(T t):对给定的参数进行判断,返回一个布尔值 - Stream distinct(): 返回由该流的不同元素(根据Object.equals(Object) )组成的流
- Stream limit(long maxSize): 返回此流中的元素组成的流,截取前指定参数个数的数据
- Stream skip(long n):跳过指定参数个数的数据,返回由该流的剩余元素组成的流
映射
- Stream map(Function mapper):返回由给定函数应用于此流的元素的结果组成的流
Function接口中的方法 R apply(Tt) - IntStream mapToInt(ToIntFunction mapper):返回一个IntStream其中包含将给定函数应用于此流的元素的结果。IntStream:表示原始int流
- flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
排序
-
Stream sorted():返回由此流的元素组成的流,根据自然顺序排序
-
Stream sorted(Comparator comparator):返回由该流的元素组成的流,根据提供的Comparator进行排序
-
static Stream concat(Stream a, Stream b):合并a和b两个流为一个流
代码示例:
package stream;
import java.util.ArrayList;
public class StreamDemo01 {
public static void main(String[] args) {
//创建一个集合, 存储多个字符串元素
ArrayList<String> list = new ArrayList<String>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
list.add("张敏");
list.add("张无忌");
//需求1:把list集合中以张开头的元素在控制台输出
list.stream().filter(s->s.startsWith("张")).forEach(System.out::println);
System.out.println("---------------");
//需求2:把list集合中长度为3的元素在控制台输出
list.stream().filter(s->s.length()==3).forEach(System.out::println);
System.out.println("---------------");
//需求3:把list集合中以张开头且长度为3的元素在控制台输出
list.stream().filter(s->s.startsWith("张")).filter(s->s.length()==3).forEach(System.out::println);
System.out.println("==========================");
//需求4:取前3个数据输出
list.stream().limit(3).forEach(System.out::println);
System.out.println("---------------");
//需求5:跳过三个元素,剩下输出
list.stream().skip(3).forEach(System.out::println);
System.out.println("---------------");
//需求6:跳过2个元素、取前2个数据 输出 list.stream().skip(2).limit(2).forEach(System.out::println);
System.out.println("====================");
//排序list中的顺序并在控制台输出
list.stream().sorted().forEach(System.out::println);
System.out.println("====================");
//按照字符串长度 把数据在控制台中输出
list.stream().sorted((s1, s2)->{
int num = s1.length() - s2.length();
int num2 = num == 0 ? s1.compareTo(s2) : num;
return num2;
}).forEach(System.out::println);
}
}
代码示例2:
package stream;
import java.util.ArrayList;
public class StreamDemo02 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("10");
list.add("20");
list.add("30");
list.add("40");
list.add("50");
list.stream().map(s->Integer.parseInt(s)).forEach(System.out::println); list.stream().map(Integer::parseInt).forEach(System.out::println); list.stream().mapToInt(Integer::parseInt).forEach(System.out::println);
//返回 此流中的 元素的总和
int result = list.stream().mapToInt(Integer::parseInt).sum();
System.out.println(result);
}
}
常见终结操作方法
- 终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void。
- 流进行了终止操作后,流就被消费,不能再次使用。
匹配与查找
- allMatch(Predicate p) 检查是否匹配所有元素
- anyMatch(Predicate p) 检查是否至少匹配一个元素
- noneMatch(Predicate p) 检查是否没有匹配所有元素
- findFirst() 返回第一个元素
- findAny() 返回当前流中的任意元素
- count() 返回流中元素总数
- max(Comparator c) 返回流中最大值
- min(Comparator c) 返回流中最小值
- forEach(Consumer c) 内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮你把迭代做了)
归约
reduce(T identity, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 T
reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 Optional
备注:map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它来进行网络搜索而出名。
收集
- collect(Collector c) 将流转换为其他形式。接收一个 Collector 接口的实现,用于给 Stream 中元素做汇总的方法
Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、Map)。
另外, Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下:
工具类Collectors提供了具体的收集方式 - public static Collector toList():把元素收集到List集合中
- public static Collector toSet():把元素收集到Set集合中
- public static Collector toMap(Function keyMapper, Function valueMapper):把元素收集到Map集合中
- 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> 根据比较器选择最大值
Optionalmax=list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)));
- minBy Collector<T, ?, Optional> 根据比较器选择最小值
Optional<Emp> min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary)));
- reducing Collector<T, ?, Optional> 从一个作为累加器的初始值开始,利用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>> 根据某属性值对流分组,属性为K,结果为 V
Map<Emp.Status, List<Emp>> map= list.stream().collect(Collectors.groupingBy(Employee::getStatus));
- partitioningBy Collector<T, ?, Map<Boolean, List>> 根据 true 或 false 进行分区
Map<Boolean,List<Emp>> vd = list.stream().collect(Collectors.partitioningBy(Employee::getManage));
Stream<String> listStream = list.stream().filter(s->s.startsWith("张"));
List<String> names = listStream.collect(Collectors.toList());
练习
//定义一个字符串数组,每一个字符串数据由姓名数据和年龄数据组合而成
String[] strArray2 = {“林青霞,30”, “张曼玉,35”,“王祖贤,33”, “柳岩,25”};
//得到字符串中年龄数据大于28的流
Stream<String> arrayStream = Stream.of(strArray).filter(s -> Integer.parseInt(s.split(",")[1]) > 28);
//需求:把使用Stream流操作完毕的数据收集到Map集合中并遍历,字符串中的姓名作键,年龄作值
Map<String, Integer> map2 = arrayStream.collect(Collectors.toMap(s -> s.split(",")[0],s->Integer.parseInt(s.split(",")[1]);
Set<String> keySet = map.keySet();
for (String key : keySet) {
Integer value = map.get(key);
System.out.println(key + "," + value);
}
Java9新增API
1. Stream 实例化方法
ofNullable() 方法
在 Java 8 中,使用 Stream.of(null) 会引发空指针异常。而在 Java 9 中,ofNullable() 方法允许创建一个单元素的 Stream,可以包含非空元素或创建一个空 Stream。
// Java 8 可能报 NullPointerException
// Stream<Object> stream1 = Stream.of(null);
// System.out.println(stream1.count()); // 报错
// Java 9 允许 null 值
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()); // 1
iterator() 方法重载
在 Java 9 中,Stream.iterate() 进行了重载,允许指定终止条件。
复制
// 原来的控制终止方式
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);