目前(2023/1/5),java已经迭代到了 Java 19,Java 20也将在2023年3月发布。
迭代了这么多版本,每个版本都更新了哪些新特性?
现在谈jdk8新特性,是不是有点晚了,但是对于一直停留在 jdk7 & jdk8 和我一样的程序猿也很多吧。对于jdk一些特性,还是很有必要去了解一下,不至于在阅读一些新框架的时候,看不懂源码。
新特性
- Lambda表达式
- 函数式接口
- 方法引用与构造器引用
- Stream API
- ...
其中,使用最广泛的特性是Lambda表达式和Stream API。
优点
- 速度更快
- 代码量更少
- 强大的Stream API
- 便于并行
- 减少空指针异常
下面主要看一下Lambda表达式和强大的Stream API,这也是我在开发中,用的比较多的新特性。
Lambda表达式
先了解一下函数式接口:指的是有且仅有一个抽象方法的接口就称为函数式接口。
Lambda是一个匿名函数,感觉上是把一段代码赋给一个变量。其实所有的Lambda的类型都是一个接口,Lambda表达式本身就是一个接口的实现。那么Lambda表达式是怎么简化来的呢?
先上个栗子,感觉一下:
- 平时方式实现Runnable
new Thread((new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
}));
- Lambda方式实现Runnable
Runnable runnable = () -> System.out.println("hello !");
new Thread(runnable).start();
可能直接拿创建线程的栗子来说,还不够简单,上面说过Lambda表达式本身就是一个接口的实现,下面简单定义一个接口。
public interface MyInterface {
void print();
}
- 原本实现接口的方式
public class MyInterfaceImpl implements MyInterface {
@Override
public void print() {
System.out.println("hello !");
}
}
- Lambda方式实现接口
MyInterface myInterface = (name -> System.out.println("hello " + name +" !"));
myInterface.print("shiyunxi");
使用Labmda表达式需要函数式编程接口,比如在Runnable接口上我们可以看到@FunctionalInterface注解(@FunctionalInterface注解不是必须的,但建议最好加上,这样可以通过编译器来检查接口中是否仅存在一个抽象方法)。如果接口出现两个方法,会报错。
Stream
Stream流不是一种数据结构,不保存数据,而是对数据进行加工处理,像写sql一样地去操作集合数据。
操作步骤
- 创建Stream:根据集合或数组,获取一个流。
- 中间操作:中间操作链,对数据进行处理。
- 终止操作:执行完中间操作链,产生最终结果数据。
Stream API也是开发中最常用的,比如说集合筛选过滤、排序、分组等操作,下面直接上干货。
中间操作
流方法 | 含义 |
---|---|
filter | 用于通过设置的条件过滤出元素 |
distinct | 返回一个元素各异(根据流所生成元素的hashCode和equals方法实现)的流。 |
limit | 会返回一个不超过给定长度的流。 |
skip | 返回一个扔掉了前n个元素的流。 |
map | 接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素。 |
flatMap | 使用flatMap方法的效果是,各个数组并不是分别映射成一个流,而是映射成流的内容。所有使用map(Arrays::stream)时生成的单个流都被合并起来,即扁平化为一个流。 |
sorted | 返回排序后的流 |
基础操作
List<Integer> list = Arrays.asList(3,4,6,1,4,6,8,10,11,1,2);
//过滤:大于5的数据
List<Integer> filter = list.stream().filter(n -> n > 5).collect(Collectors.toList());
//去重
List<Integer> distinct = list.stream().distinct().collect(Collectors.toList());
//截取前3个
List<Integer> limit = list.stream().limit(3).collect(Collectors.toList());
//跳过前4个
List<Integer> skip = list.stream().skip(4).collect(Collectors.toList());
//排序
List<Integer> sorted = list.stream().sorted().collect(Collectors.toList());
map操作
List<String> stringList = Arrays.asList("hello", "world");
List<String[]> map = stringList.stream()
.map(str -> str.split(""))
.distinct().collect(Collectors.toList());
List<String> flatmap = stringList.stream()
.map(str -> str.split(""))
.flatMap(Arrays::stream)
.distinct()
.collect(Collectors.toList());
reduce操作
//求和
Integer sum = list.stream().reduce(0, (x, y) -> x + y);
查找和匹配
//所有的元素都大于0
boolean b = list.stream().allMatch(x -> x > 0);
//和allMatch相反,所有的都不是返回true
boolean b1 = list.stream().noneMatch(x -> x > 100);
//存在大于10的元素
boolean b2 = list.stream().allMatch(x -> x > 10);
// 返回第一个元素
Integer first = list.stream().findFirst().get();
// 随机返回一个
Integer any = list.stream().findAny().get();
分组
Student student1 = new Student("s1", 18, "101");
Student student2 = new Student("s2", 21, "101");
Student student3 = new Student("s3", 14, "101");
Student student4 = new Student("s4", 22, "102");
Student student5 = new Student("s5", 23, "102");
Student student6 = new Student("s6", 19, "103");
Student student7 = new Student("s7", 20, "103");
List<Student> studentList = Stream.of(student1, student2, student3, student4, student5, student6, student7).collect(Collectors.toList());
//按照班级分组
Map<String, List<Student>> collect = studentList.stream().collect(Collectors.groupingBy(Student::getClassCode));
//{101=[Student{name='s1', age=18, classCode='101'}, Student{name='s2', age=21, classCode='101'}, Student{name='s3', age=14, classCode='101'}], 102=[Student{name='s4', age=22, classCode='102'}, Student{name='s5', age=23, classCode='102'}], 103=[Student{name='s6', age=19, classCode='103'}, Student{name='s7', age=20, classCode='103'}]}
//按照班级分组,获取姓名
Map<String, List<String>> collect1 = studentList.stream().collect(Collectors.groupingBy(Student::getClassCode, Collectors.mapping(Student::getName, Collectors.toList())));
//{101=[s1, s2, s3], 102=[s4, s5], 103=[s6, s7]}
//统计每个班级的人数
Map<String, Long> collect2 = studentList.stream().collect(Collectors.groupingBy(Student::getClassCode, Collectors.counting()));
//{101=3, 102=2, 103=2}
//统计每个班的平均年龄
Map<String, Double> collect3 = studentList.stream().collect(Collectors.groupingBy(Student::getClassCode, Collectors.averagingDouble(Student::getAge)));
//{101=17.666666666666668, 102=22.5, 103=19.5}
//每个学生年龄+10,并且返回每个班级的姓名和新年龄
Map<String, Map<String, Integer>> collect4 = studentList.stream().collect(Collectors.groupingBy(Student::getClassCode, Collectors.toMap(Student::getName, student -> student.getAge() + 10)));
//{101={s3=24, s1=28, s2=31}, 102={s4=32, s5=33}, 103={s6=29, s7=30}}
//嵌套分组,先按照班级,再按照年龄
Map<String, Map<Integer, List<Student>>> collect5 = studentList.stream().collect(Collectors.groupingBy(Student::getClassCode, Collectors.groupingBy(Student::getAge)));
//{101={18=[Student{name='s1', age=18, classCode='101'}], 21=[Student{name='s2', age=21, classCode='101'}], 14=[Student{name='s3', age=14, classCode='101'}]}, 102={22=[Student{name='s4', age=22, classCode='102'}], 23=[Student{name='s5', age=23, classCode='102'}]}, 103={19=[Student{name='s6', age=19, classCode='103'}], 20=[Student{name='s7', age=20, classCode='103'}]}}