JAVA8 新特性
[TOC]
# JAVA8 新特性
# Lambda 表达式
# 基本介绍
JAVA8中引入了一个新的操作符 “->”,该操作符称为箭头操作符或 Lambda 操作符。
箭头操作符将 Lambda 表达式拆分成两部分:
左侧:Lambda 表达式的参数列表
右侧:Lambda 表达式中所需执行的功能,即 Lambda 体
注意事项:
Lambda 表达式需要“函数式接口”的支持
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 语法格式
# 格式一:无参数,无返回值
// 语法:() -> System.out.println("Hello Lambda");
@Test
public void test1(){
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Holle JAVA");
}
};
r.run();
System.out.println("-----------------------------------");
Runnable r1 = () -> System.out.println("Hello Lambda");
r1.run();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 格式二:有参数,无返回值
/**
* 格式二:有参数,无返回值 * (x) -> System.out.println(x);
*/
@Test
public void test2(){
Consumer<String> con = new Consumer<String>() {
@Override
public void accept(String s){
System.out.println(s);
}
};
con.accept("Hello JAVA");
System.out.println("---------------------------------");
Consumer<String> con1 = (x)-> System.out.println(x);
con1.accept("Hello Lambda");
System.out.println("---------------------------------");
// 若只有一个参数,小括号也可以省略不写
Consumer<String> con1 = x -> System.out.println(x);
con1.accept("Hello 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
25
26
27
28
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
# 格式三:有两个或以上的参数,有返回值,并且Lambda 体中有多条语句
/**
* 格式三:有两个或以上的参数,有返回值,并且Lambda 体中有多条语句
* Comparator<Integer> com = (x,y) -> {
* System.out.println("Lambda...");
* return Integer.compare(x,y);
* };
*/
@Test
public void test3(){
Comparator<Integer> com = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1,o2);
}
};
System.out.println("-------------------------------------");
Comparator<Integer> com = (x,y) -> {
System.out.println("Lambda...");
return Integer.compare(x,y);
};
System.out.println("-------------------------------------");
//如果Lambda 体中只有一条语句, 大括号和 return 都可以省略不写
Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
}
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
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
# 函数式接口
# 基本概念
概念:只包含一个抽象方法的接口,称之为函数式接口
可以通过 Lambda 表达式来创建该接口的对象。
注意:如果 Lambda 表达式抛出一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明。
可以在接口上使用 @FunctionalInterface 注解,如果没有报错,则证明该接口为函数式接口。
1
2
3
4
5
6
7
2
3
4
5
6
7
# 四大核心函数式接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer | T | void | 对类型为T的对象应用操作 |
Supplier | 无 | T | 返回类型为T的对象。 |
Function<T,R> 函数型接口 | T | R | 对类型为T的对象应用操作,并返回结果,结果是R类型的对象。 |
Predicate | T | boolean | 确定类型为T的对象是否满某约束,并返回 boolean 值。 |
# 示例
@Test
void test3() {
//旧
consumerTest(200, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println("周末花了 " + aDouble + " 块");
}
});
System.out.println("--------------------------------------------------");
//Lambda
consumerTest(200,money -> System.out.println("花超了 " + money + " 块"));
}
public void consumerTest(double money, Consumer<Double> con){
con.accept(money);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 方法引用
# 基本概念
概念:
当要传递给Lambda体的操作,已经有实现的方法了,就可以使用方法引用。
方法引用可以看作是 Lambda 表达式深层次的表达。换句话说,方法引用就是 Lambda 表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是 Lambda 表达式的一个语法糖。
要求:
实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致!
格式:
使用操作符 “ :: ”将类(或对象)与方法名分隔开来。
三种使用情况:
-> 对象 :: 非静态方法
-> 类 :: 静态方法
-> 类 :: 非静态方法
使用的要求:
要求接口中的抽象方法上的形参列表和返回值类型
必须与方法引用的方法上的形参列表和返回值类型相同!(针对前两种使用情况)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 对象 :: 非静态方法
@Test
void test3() {
Consumer<String> con1 = str -> System.out.println(str);
con1.accept("芜湖");
System.out.println("------------------------------------------");
Consumer<String> con2 = System.out::println;
con1.accept("起飞");
}
@Test
void test4() {
PmsProduct ppt = new PmsProduct();
ppt.setName("小明");
Supplier<String> sup1 = () -> ppt.getName();
System.out.println(sup1.get());
System.out.println("---------------------------------------------");
Supplier<String> sup2 = ppt::getName;
System.out.println(sup2);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 类 :: 静态方法
@Test
void test3() {
Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2);
System.out.println("com1 = " + com1.compare(1,2));
System.out.println("-------------------------------------------");
Comparator<Integer> com2 = Integer::compareTo;
System.out.println("com2 = " + com2.compare(2,1));
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 类 :: 非静态方法
@Test
void test2() {
Comparator<String> com1 = (t1,t2) -> t1.compareTo(t2);
System.out.println(com1.compare("abc","aaa"));
System.out.println("----------------------------");
Comparator<String> com2 = String::compareTo;
System.out.println(com2.compare("aaa","bbb"));
}
@Test
void test3() {
BiPredicate<String,String> pre1 = (s1,s2) -> s1.equals(s2);
System.out.println(pre1.test("aaa","bbb"));
System.out.println("----------------------------------------");
BiPredicate<String,String> pre2 = String::equals;
System.out.println(pre2.test("aaa","aaa"));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 构造器引用 和 数组引用
# 构造器引用基本使用
// 构造器引用和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。抽象方法的返回值类型即为构造器所属的类的类型。
@Test
void test3() {
Supplier<PmsProduct> sup = new Supplier<PmsProduct>() {
@Override
public PmsProduct get() {
return new PmsProduct();
}
};
System.out.println("****************************************");
Supplier<PmsProduct> sup1 = () -> new PmsProduct();
System.out.println("****************************************");
Supplier<PmsProduct> sup2 = PmsProduct::new;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 数组引用基本使用
@Test
void test3() {
Function<Integer, String[]> fun1 = len -> new String[len];
String[] arr1 = fun1.apply(5);
System.out.println(arr1.length);
Function<Integer, String[]> fun2 = String[]::new;
String[] arr2 = fun2.apply(6);
System.out.println("arr2 = " + arr2.length);
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# Stream API
# 为什么要用 Stream API
在目前的实际开发中,我们更多的数据处理,计算等都会放在 JAVA 层面去进行,而Stream API 就是为此而出现。
Stream 是 java8 中用于处理集合、数组,包括 过滤,排序,遍历等操作。
Stream 和 Collection 集合的区别:
1、Collection 是一种静态的内存数据结构,而 Stream 则是有关计算的。
2、Collection 主要面向内存,存储在内存中; Stream 则是面向 CPU,通过 CPU 实现计算。
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 什么是 Stream
Stream 是一种数据渠道,用于操作数据源(集合、数组等)所生成的元素序列
注意:
1、Stream 不会存储元素
2、Stream 不会改变源对象。相反,他们会返回一个持有结果的新 Stream
3、Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
1
2
3
4
5
6
7
2
3
4
5
6
7
# Stream 的创建
# 创建方式一:集合
@Test
public void test4(){
// 通过集合可以创建两种 Stream ,串行流和并行流
// 拿到一个用于测试的集合,该集合中有 1~12 个数字
List<Integer> numbers = StudentData.getNumbers();
// 返回一个串行流,即有顺序的
Stream<Integer> stream = numbers.stream();
stream.forEach(a -> System.out.println(a));
System.out.println("-------------------------------------------------------");
// 返回一个并行流,即无序的
Stream<Integer> bookStream = numbers.parallelStream();
bookStream.forEach(b -> System.out.println(b));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 创建方式二:数组
@Test
public void test5(){
int[] arr = new int[] {1,2,3,4,5,6,7,8};
//调用 Arrays 类的 Stream 返回一个流
IntStream stream = Arrays.stream(arr);
}
1
2
3
4
5
6
2
3
4
5
6
# 创建方式三:通过 Stream 的 of()
@Test
public void test6(){
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5);
}
1
2
3
4
2
3
4
# 创建方式四:无限流
@Test
public void test7(){
/**
* public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
* UnaryOperator:一个函数式接口,将传入的对象进行运算,并返回结果
* 传入数字 1,迭代十个数。
*/
Stream.iterate(1,t -> t+1).limit(10).forEach(System.out::println);
System.out.println("--------------------------------------------------");
/**
* public static<T> Stream<T> generate(Supplier<T> s)
* 生成并打印十个随机数
*/
Stream.generate(Math::random).limit(10).forEach(System.out::println);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 筛选
@Test
public void test8() {
List<Integer> numbers = StudentData.getNumbers();
//filter : 过滤,可以使用 Lambda 表达式,从流中过滤出符合条件的数据
// numbers.stream().filter(i -> i>7 ).forEach(System.out::println);
// limit :截断流,使其元素不超过给定的数量
// numbers.stream().limit(3).forEach(System.out::println);
// skip :跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流
// numbers.stream().skip(10).forEach(System.out::println);
// distinct :筛选,通过流所生成元素的 hashCode 和 equals 两个方法,去除重复的元素
//为了体现效果,增加三个重复元素到集合中
numbers.add(10);
numbers.add(11);
numbers.add(12);
numbers.stream().forEach(System.out::println);
System.out.println("-------- 对比效果 ----------");
numbers.stream().distinct().forEach(System.out::println);
}
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 映射
@Test
public void test9() {
// map(Function f):接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素。
List<String> list = Arrays.asList("a", "b", "c", "d");
list.stream().map(str -> {
switch (str) {
case "a":
return 1;
case "b":
return 2;
case "c":
return 3;
case "d":
return 4;
default:
return 0;
}
}).forEach(System.out::println);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 额外扩展:flatMap
@Test
public void test10(){
// flatMap(Function f):接收一个函数作为参数,将流中的每个值都换成另一个流。
// flatMap 和 map 的区别
List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
//map
Stream<Stream<Character>> stream = list.stream().map(LambdaTest::fromStringToStream);
stream.forEach(str -> {
str.forEach(System.out::println);
});
System.out.println("------------------ 分割线 --------------------");
//flatMap
Stream<Character> stream1 = list.stream().flatMap(LambdaTest::fromStringToStream);
stream1.forEach(System.out::println);
}
public static Stream<Character> fromStringToStream(String str){
List<Character> list = new ArrayList<>();
for (Character c : str.toCharArray()){
list.add(c);
}
return list.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
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
# 排序
@Test
public void test11() {
// sorted :自然排序,只适用于基本元素的比较
List<Integer> list = Arrays.asList(11,30,2,5,60,44,22);
list.stream().sorted().forEach(System.out::println);
System.out.println("-------------------- 分割线 -------------------");
// sorted(Comparator<? super T> comparator):可以用于指定自己想要的排序
List<Student> students = StudentData.getStudents();
students.stream().sorted((a1, a2) -> Integer.compare(a1.getAge(),a2.getAge())).forEach(System.out::println);
System.out.println("-------------------- 分割线 -------------------");
//还可以这样写
students.stream().sorted(Comparator.comparing(Student::getAge)).forEach(System.out::println);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 查找与匹配
@Test
public void test12(){
List<Integer> list = Arrays.asList(11,30,2,80,60,44,22);
// allMatch:检查是否匹配所有元素。
// 是否所有元素都大于 5
boolean allMatch = list.stream().allMatch(a -> a > 5);
System.out.println("allMatch = " + allMatch);
// anyMatch:检查是否至少匹配一个元素
// 是否存在大于 5 的元素
boolean anyMatch = list.stream().anyMatch(a -> a > 5);
System.out.println("anyMatch = " + anyMatch);
// noneMatch:检查是否没有匹配的元素。没有就会返回 true ,有则返回 false
// 是否存在数字 50
boolean noneMatch = list.stream().noneMatch(a -> a == 50);
System.out.println("noneMatch = " + noneMatch);
// findFirst:返回第一个元素
Optional<Integer> first = list.stream().findFirst();
System.out.println("first = " + first);
// findAny:返回当前流中的任意元素
Optional<Integer> any = list.parallelStream().findAny();
System.out.println("any = " + any);
// count:返回流中元素的总个数
// 查询大于 11 的元素有多少个
long count = list.stream().filter(a -> a > 11).count();
System.out.println("count = " + count);
// max:返回流中最大值
Optional<Integer> max = list.stream().max(Integer::compareTo);
System.out.println("max = " + max);
// min:返回流中最小值
Optional<Integer> min = list.stream().min(Integer::compareTo);
System.out.println("min = " + min);
// forEach:内部迭代,说人话就是遍历
list.stream().forEach(System.out::println);
}
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
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
# 归约(递归运算)
@Test
public void test13(){
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
// reduce(T identity, BinaryOperator<T> accumulator):可以将流中元素反复结合起来,得到一个值并返回。
//初始值为0.计算 1-10 的自然数的和
Integer reduce = list.stream().reduce(0, Integer::sum);
System.out.println("reduce = " + reduce);
System.out.println("------------------ 分割线 -------------------");
// reduce(BinaryOperator<T> accumulator)
// 不设定初始值,计算所有元素的总和
list.stream().reduce(Integer::sum);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 收集(转化)
@Test
public void test14() {
List<Student> list = StudentData.getStudents();
// collect(Collector c):将流转换为其他形式。接收一个 Collector 接口的实现,用于给 Stream 中元素做汇总的方法
List<Integer> integerList = list.stream().map(Student::getAge).collect(Collectors.toList());
System.out.println("Collectors.toList()");
integerList.stream().forEach(System.out::println);
System.out.println("-----------------------------------------------------------");
Set<Integer> integerSet = list.stream().map(Student::getAge).collect(Collectors.toSet());
System.out.println("Collectors.toSet()");
integerSet.stream().forEach(System.out::println);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
帮我改善此页面 (opens new window)
上次更新: 2021/07/06, 07:48:41