Java8新特性

Java 最新 LTS 已经到 17 了,面试的时候如果 JAVA8 都不会的话,感觉很难接受

Lambda 表达式

一个匿名函数、可以理解为一段可以传递的代码。可以写出更简洁、更灵活的代码

本质:函数式接口的实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Test{
@Test
public void test2(){
//普通写法
Comparator<Integer> com = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
int compare = com.compare(11,21);
System.out.println(compare);

//lambda写法
Comparator<Integer> com2 = (o1,o2) -> Integer.compare(o1, o2);
int compare2 = com2.compare(21, 25);
System.out.println(compare2);

//方法引用
Comparator<Integer> com3 = Integer :: compare;
int compare3 = com3.compare(51,32);
System.out.println(compare3);
}
}
  • 语法规则
1
2
3
4
5
- 无参无返回值,参数为一个空的小括号
- 数据类型可以省略,由编译器推断,称为类型判断
- 若只需要一个参数,参数小括号可以省略
- 两个以上参数,多条语句,两个括号都不能省略
- 有且只有一条返回语句时,return 和打括号都可以一起省略

函数式接口

有且仅有一个抽象方法的接口

加一个 @FunctionalInterface,JavaDoc 中会表明表示这是一个函数式接口,可以进行校验

四大内置函数式接口

接口参数返回类型用途方法
ConsumerTvoid对类型 T 对应用操作void accept()
SupplierT返回类型为 T 对对象T get()
FunctionTR对类型为 T 对应用操作,并返回结果R apply(T t)
PredicateTboolean确定 T 是否满足约束,并返回 booleanboolean test(T t)
  • 例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class TestLambda3 {
@Test
public void test2(){
List<String> list = Arrays.asList("北京", "南京", "西京", "北京", "普京","武汉","深圳");

List<String> filterStrs = filterString(list, new Predicate<String>() {
@Override
public boolean test(String s) {
return s.contains("京");
}
});
List<String> filterStrs2 = filterString(list, s -> s.contains("京"));

System.out.println(filterStrs);
System.out.println(filterStrs2);
}
public List<String> filterString(List<String> list, Predicate<String> pre){
ArrayList<String> filter = new ArrayList<>();
for(String s : list) {
if(pre.test(s)){
filter.add(s);
}
}
return filter;
}
}

引用

方法引用

当要传递给 Lambda 体的操作已经有实现的时候,可以使用方法引用

要求:实现接口的抽象方法和方法引用的方法有相同的参数列表和返回值类型

格式:类(或对象)::方法名

  • 1、对象::实例方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class TestMethodRef {
//对象::实例方法
//Consumer void accept(T t)
//PrintStream void println(T t)
@Test
public void test1(){
Consumer<String> con1 = str -> System.out.println(str+"...");
con1.accept("北京");

PrintStream ps = System.out;
Consumer<String> con2 = ps::println;
con2.accept("beijing");
}
//Supplier中的get()
//Employee的String getName()
@Test
public void test2(){
Employee emp = new Employee("tom", 24, 5600);
Supplier<String> sup1 = () -> emp.getName();
System.out.println(sup1.get());

Employee emp2 = new Employee("tom", 24, 5600);
Supplier<String> sup2 = emp::getName;
System.out.println(sup2.get());
}
}
  • 类::静态方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class TestStatic{
//类:静态方法
//Comparator int compare(t1,t2)
//Integer int compare(t1,t2)
@Test
public void test3(){
Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2);
System.out.println(com1.compare(12,21));

Comparator<Integer> com2 =Integer::compare;
System.out.println(com2.compare(12,32));
}

@Test
public void test4(){
Function<Double, Long> function = new Function<Double, Long>() {
@Override
public Long apply(Double d) {
return Math.round(d);
}
};
Function<Double, Long> function1 = d ->Math.round(d);
Function<Double, Long> function2 = Math::round;
}
}
  • 类::实例方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class Test{
//类::实例方法(有难度)
//Comparator int compare(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;
}


//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("1","1"));

System.out.println("***************");

BiPredicate<String,String> pre2 = String :: equals;

System.out.println(pre2.test("ab","ab"));
}
//Function R apply(T t)
//Employee String getName()
@Test
public void test7(){
Employee employee = new Employee("lqs", 24, 9600);
Function<Employee, String> function = e -> e.getName();
System.out.println(function.apply(employee));

System.out.println("***************");

Function<Employee, String> function1 = Employee::getName;
System.out.println(function1.apply(employee));
}
}

构造器引用

类似方法引用,函数式接口的抽象方法和构造器有相同的形参列表,就可以使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class TestConstructorRef {
@Test
public void test(){
Supplier<Employee> sup = new Supplier<Employee>() {
@Override
public Employee get() {
return new Employee();
}
};

Supplier<Employee> sup1 = () -> new Employee();
//构造器引用
Supplier<Employee> sup2 = Employee::new;

System.out.println(sup2.get());
}

@Test
public void test2(){
Function<String ,Employee> func1 = name -> new Employee(name);
Employee employee = func1.apply("lqs");
System.out.println(employee);


Function<String,Employee> func2 = Employee :: new;
Employee employee1 = func2.apply("lcy");
System.out.println(employee1);
}

@Test
//BiFunction R apply(T t, U u)
public void test3(){
BiFunction<String, Integer, Employee> func1 = (name, age) -> new Employee(name,age);
System.out.println(func1.apply("lqs", 21));

BiFunction<String, Integer, Employee> func2 = Employee :: new;
System.out.println(func2.apply("jj", 21));
}
}

数组引用

把数组看做特殊的类,可以使用引用

1
2
3
4
5
6
7
8
9
10
11
12
public class TestArrayRef {
@Test
public void test(){
Function<Integer,String[]> func1 = length -> new String[length];
String[] arr1 = func1.apply(5);
System.out.println(Arrays.toString(arr1));

Function<Integer, String[]> func2 = String[]::new;
String[] arr2 = func2.apply(10);
System.out.println(Arrays.toString(arr2));
}
}

Stream API

是数据渠道,用于操作数据源(集合、数组)所生成的元素序列

Stream 不会自己存储元素;不会改变源对象,而是返回一个持有结果的新 Stream;操作是延迟执行的,会等到需要新结果的时候才执行。

Stream && Collection:Collection 是一种静态的内存数据,Stream 有关计算,面向 CPU

三个步骤

创建 Stream

一个数据源(如数组,集合),获取一个流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class TestStreamCreate {
//通过集合创建
@Test
public void test(){
List<Employee> list = new ArrayList<>();
for(int i = 0; i < 10; i++){
list.add(new Employee("name"+i, i+20, i+2000));
}
//获取流
Stream<Employee> stream = list.stream();
//返回一个并行流
Stream<Employee> parallelsStream = list.parallelStream();
}

//通过数组
@Test
public void test2(){
int[] arr = new int[]{1,2,3,4,5,76};
IntStream stream = Arrays.stream(arr);
int row = 10;
Employee[] employees = new Employee[row];
for(int i = 0; i < row; i++){
employees[i] = new Employee("name"+i, i+20, i+2000);
}
Stream<Employee> stream1 = Arrays.stream(employees);
}

//通过Stream对of()
@Test
public void test3(){
Stream<Integer> stream = Stream.of(1,2,3,5,7,8,90);
}

//创建无限流
//limit为了限制
@Test
public void test4(){
//迭代
//Stream<T> iterate(final T seed), final UnaryOperator<T> f)
//遍历前10个偶数
Stream.iterate(0, t -> t+2).limit(10).forEach(System.out::println);
//生成
//Stream<T> generate(Supplier<T> s)
//前10个随机数
Stream.generate(Math::random).limit(10).forEach(System.out::println);
}
}

中间操作

对数据源进行处理

  • 1、筛选与切片

    方法描述
    filter(Predicate p)接受 lambda,从流中排除某些元素
    distinct()筛选,通过流锁生成对 hashCode()和 equals()去除重复元素
    limit(long maxSize)截断流,使其元素不超过给定数量
    skip(long n)跳过元素,返回一个扔掉了前 n 个元素对流;不足 n 个是返回空流;与 limit 互补
    方法描述
    --
    filter(Predicate p)接受 lambda,从流中排除某些元素
    distinct()筛选,通过流锁生成对 hashCode()和 equals()去除重复元素
    limit(long maxSize)截断流,使其元素不超过给定数量
    skip(long n)跳过元素,返回一个扔掉了前 n 个元素对流;不足 n 个是返回空流;与 limit 互补
  • 例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class TestStreamDeal {
@Test
public void test(){
List<Employee> list = new ArrayList<>();
for(int i = 0; i < 10; i++){
list.add(new Employee("name"+i, i+20, i+2000));
}

//filter:选择
Stream<Employee> stream = list.stream();
//查询工资 > 2005的员工
stream.filter(employee -> employee.getSalary() > 2005).forEach(System.out::println);

System.out.println("***************");

//limit:截断
Stream<Employee> stream2 = list.stream();
stream2.limit(5).forEach(System.out::println);

System.out.println("***************");

//distinct:去重
int k = 7;
list.add(new Employee("name"+k, 20+k, 2000+k));
Stream<Employee> stream3 = list.stream();
stream3.distinct().forEach(System.out::println);

}
}
  • 2、映射

    方法描述
    map(Function f)接受一个函数为参数,该函数作用到每个元素,并将其映射为一个新的元素
    mapToDouble(ToDoubleFunction f)接受一个函数为参数,该函数作用到每个元素,并产生一个新的 DoubleStream
    mapToInt(ToIntFunction f)接受一个函数为参数,该函数作用到每个元素,并产生一个新的 IntStream
    mapToLong(ToLongFunction f)接受一个函数为参数,该函数作用到每个元素,并产生一个新的 LongStream
    flatMap(Function f)接受一个函数为参数,将流中每个值转换成另一个流,然后所有流连接成一个流
  • 例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
public class TestStreamDeal {
@Test
public void test(){
List<Employee> list = EmployeeData.getEmp();

//filter:选择
Stream<Employee> stream = list.stream();
//查询工资 > 24000的员工
stream.filter(employee -> employee.getSalary() > 2005).forEach(System.out::println);

System.out.println("***************");

//limit:截断
Stream<Employee> stream2 = list.stream();
stream2.limit(5).forEach(System.out::println);

System.out.println("***************");

//distinct:去重
int k = 7;
list.add(new Employee("name"+k, 20+k, 2000+k));
Stream<Employee> stream3 = list.stream();
stream3.distinct().forEach(System.out::println);

}

@Test
public void test2(){
List<String> list = Arrays.asList("aa","bb","cc","dd");
//全部大写
//lambda写法
list.stream().map(s -> s.toUpperCase()).forEach(System.out::println);
//引用写法
list.stream().map(String::toUpperCase).forEach(System.out::println);

System.out.println("*********************");

//练习:获取员工姓名长度大于3的员工的姓名
List<Employee> emp = EmployeeData.getEmp();
// emp.stream().map(employee -> employee.getName()).filter(name -> name.length() > 3).forEach(System.out::println);
emp.stream().map(Employee::getName).filter(name -> name.length() > 3).forEach(System.out::println);

System.out.println("*********************");
//stream中嵌套stream
Stream<Stream<Character>> streamStream = list.stream().map(TestStreamDeal::stringToStream);
Stream<Character> characterStream = list.stream().flatMap(TestStreamDeal::stringToStream);
streamStream.forEach(s -> {
s.forEach(System.out::println);
});

characterStream.forEach(System.out::println);
}

//将字符串的多个字符转换为Stream的实例
public static Stream<Character> stringToStream(String str){
ArrayList<Character> list = new ArrayList<>();
for(Character c : str.toCharArray()){
list.add(c);
}
return list.stream();
}

//排序
@Test
public void test4(){
List<Integer> list = Arrays.asList(12,423,14,1,32,56,98,-1);
list.stream().sorted().forEach(System.out::println);

List<Employee> employees = EmployeeData.getEmp();
//不能直接这么写,Employee没有实现Comparable接口,需要自己定制
//employees.stream().sorted().forEach(System.out::println);
employees.stream().sorted((a,b) ->{
int ageValue = Integer.compare(a.getAge(),b.getAge());
if(ageValue != 0){
//从小到大
return ageValue;
}else {
//从大到小
return -Double.compare(a.getSalary(), b.getSalary());
}
}).forEach(System.out::println);
}
}
  • 3、排序
    方法描述
    sorted()产生一个新流,按自然顺序排序
    sorted(Comparator com)产生一个新流,按比较器顺序排序

终止操作

一旦执行终止操作,就执行中间操作链,并产生结果。之后不会再使用

  • 1、匹配查找
    方法描述
    allMatch(Predicate p)检查是否匹配所有元素
    anyMatch(Predicate p)检查是否至少匹配一个元素
    nonematch(Predicate p)检查是否没有匹配所有元素
    findFirst()返回第一个元素
    findAny()返回当前流中任意元素
    count()返回流中元素总数
    max(Comparator c)返回流中最大值
    min(Comparator c)返回流中最小值
    forEach(Comparator c)内部迭代(使用 Collection 接口需要用户自己迭代,称为外部迭代)

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class TestStreamEnd {
@Test
public void test5(){
List<Employee> list = EmployeeData.getEmp();

//是否所有员工年龄都大于24
//allMatch:检查是否所有匹配
System.out.println(list.stream().allMatch(e -> e.getAge() > 24));

//是否存在员工都工资大于24000
//anyMatch:检查是否有匹配
System.out.println(list.stream().anyMatch(employee -> employee.getSalary() > 24000));

//是否没有员工姓雷
//anyMatch:检查是否没有匹配的元素
System.out.println(list.stream().noneMatch(e -> e.getName().startsWith("雷")));

//查找第一个员工
//findFirst:返回第一个元素
System.out.println(list.stream().findFirst());
//findAny:返回任意一个
System.out.println(list.stream().findAny());
//count:返回流中元素总个数
long count = list.stream().filter(employee -> employee.getSalary() > 24000).count();
System.out.println(count);
//max:流中最大值 | min:流中最小值
Stream<Double> doubleStream = list.stream().map(Employee::getSalary);
Optional<Double> max = doubleStream.max(Double::compare);
System.out.println(max);
Optional<Employee> employee = list.stream().min(Comparator.comparingDouble(Employee::getSalary));


}
}
  • 2、规约

    方法描述
    reduce(T iden,BinaryOperator b)将流中所有元素反复结合起来,得到一个值返回,类型为 T
    reduce(BinaryOperator b)将流中所有元素反复结合起来,得到一个值返回,类型为 Optional
  • 备注:map 和 reduce 连接称为 map-reduce 模式,因 Google 用它进行网络搜索而出名

1
2
3
4
5
6
7
8
9
10
11
class Test{
@Test
public void test2(){
List<Integer> list = Arrays.asList(1,2,3,5,7,9,6,7);
list.stream().reduce(0 ,Integer::sum);
List<Employee> employees = EmployeeData.getEmp();
//计算工资总和
//流式写法
System.out.println(employees.stream().map(Employee::getSalary).reduce(Double::sum));
}
}
  • 3、收集
    方法描述
    collect(Collector c)将流转换为其他方式,接受一个 Collector 的实现,用于给 Stream 中元素做汇总的方法
1
2
3
4
5
6
7
8
9
10
class Test{
//收集器
@Test
public void test4(){
List<Employee> employees = EmployeeData.getEmp();
//找出工资大于24000的返回为list
List<Employee> employeeList = employees.stream().filter(e -> e.getSalary() > 24000).collect(Collectors.toList());
Set<Employee> collect = employees.stream().filter(e -> e.getSalary() > 24000).collect(Collectors.toSet());
}
}

Optional 类

Optional 类是一个可以为 null 的容器对象。如果值存在则 isPresent()方法会返回 true,调用 get()方法会返回该对象。

Optional 是个容器:它可以保存类型 T 的值,或者仅仅保存 null。Optional 提供很多有用的方法,这样我们就不用显式进行空值检测。

Optional 类的引入很好的解决空指针异常。

类声明

以下是一个 java.util.Optional 类的声明:

public final class Optional
extends Object

类方法

序号方法 & 描述
1**static Optional empty()**返回空的 Optional 实例。
2**boolean equals(Object obj)**判断其他对象是否等于 Optional。
3**Optional filter(Predicate predicate)**如果值存在,并且这个值匹配给定的 predicate,返回一个 Optional 用以描述这个值,否则返回一个空的 Optional。
4** Optional flatMap(Function> mapper)**如果值存在,返回基于 Optional 包含的映射方法的值,否则返回一个空的 Optional
5**T get()**如果在这个 Optional 中包含这个值,返回值,否则抛出异常:NoSuchElementException
6**int hashCode()**返回存在值的哈希码,如果值不存在 返回 0。
7**void ifPresent(Consumer consumer)**如果值存在则使用该值调用 consumer , 否则不做任何事情。
8**boolean isPresent()**如果值存在则方法会返回 true,否则返回 false。
9**Optional map(Function mapper)**如果有值,则对其执行调用映射函数得到返回值。如果返回值不为 null,则创建包含映射返回值的 Optional 作为 map 方法返回值,否则返回空 Optional。
10**static Optional of(T value)**返回一个指定非 null 值的 Optional。
11**static Optional ofNullable(T value)**如果为非空,返回 Optional 描述的指定值,否则返回空的 Optional。
12**T orElse(T other)**如果存在该值,返回值, 否则返回 other。
13**T orElseGet(Supplier other)**如果存在该值,返回值, 否则触发 other,并返回 other 调用的结果。
14** T orElseThrow(Supplier exceptionSupplier)**如果存在该值,返回包含的值,否则抛出由 Supplier 继承的异常
15**String toString()**返回一个 Optional 的非空字符串,用来调试

注意: 这些方法是从 java.lang.Object 类继承来的。


Optional 实例

我们可以通过以下实例来更好的了解 Optional 类的使用:

Tester.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Java8Tester {
public static void main(String args[]){

Java8Tester java8Tester = new Java8Tester();
Integer value1 = null;
Integer value2 = new Integer(10);

// Optional.ofNullable - 允许传递为 null 参数
Optional<Integer> a = Optional.ofNullable(value1);

// Optional.of - 如果传递的参数是 null,抛出异常 NullPointerException
Optional<Integer> b = Optional.of(value2);
System.out.println(java8Tester.sum(a,b));
}

public Integer sum(Optional<Integer> a, Optional<Integer> b){

// Optional.isPresent - 判断值是否存在

System.out.println("第一个参数值存在: " + a.isPresent());
System.out.println("第二个参数值存在: " + b.isPresent());

// Optional.orElse - 如果值存在,返回它,否则返回默认值
Integer value1 = a.orElse(new Integer(0));

//Optional.get - 获取值,值需要存在
Integer value2 = b.get();
return value1 + value2;
}
}

接口默认方法和静态方法

新日期 API

其他新特性


Java8新特性
https://polarisink.github.io/20220813/yuque/Java8新特性/
作者
Areis
发布于
2022年8月13日
许可协议