JDK1.8新特性(一) ----Lambda表达式、Stream API、函数式接口、方法引用

时间:2022-06-18 07:58:13
jdk1.8新特性知识点:
  • Lambda表达式
  • Stream API
  • 函数式接口
  • 方法引用和构造器调用
  • 接口中的默认方法和静态方法
  • 新时间日期API
  • default
 
Lambda表达式
    Lambda就是把我们之前一些复杂的代码更简单化,比如集合内容的判断比较/排序,我们之前可以进行遍历判断取出我们想要的数据或者写一个匿名内部类compareto等方法进行取出我们想要的数据,实际它们内部也就是进行了一些判断比较最终返回给了我们想要的结果。
    举例:场景一
        现在我们有三个用户 小明、小红、小强  他们的年龄分别是16, 18,20,现在我们要把这三个用户按年龄进行从小到大的排序。
        如果按原来的写法,我们首先想到的应该是 编写匿名内部类Comparator进行比较然后排序得到我们想要的结果吧,附图:
JDK1.8新特性(一) ----Lambda表达式、Stream API、函数式接口、方法引用
 JDK1.8新特性(一) ----Lambda表达式、Stream API、函数式接口、方法引用
    举例:场景二 
        我们现在要对三个用户进行筛选,得到年龄是大于16岁的用户,我们首先想到的应该是进行遍历集合进行判断筛选,最后放入到一个新的集合吧,附图:
JDK1.8新特性(一) ----Lambda表达式、Stream API、函数式接口、方法引用

现在JDK1.8给我们提供了新的方式Lambda表达式,比上边的两个例子编写的代码更为简单更简介,下面我们来看一看怎么比上边的代码更简单。

    场景一:附图
JDK1.8新特性(一) ----Lambda表达式、Stream API、函数式接口、方法引用
JDK1.8新特性(一) ----Lambda表达式、Stream API、函数式接口、方法引用
    场景二:附图
JDK1.8新特性(一) ----Lambda表达式、Stream API、函数式接口、方法引用

JDK1.8新特性(一) ----Lambda表达式、Stream API、函数式接口、方法引用

JDK1.8新特性(一) ----Lambda表达式、Stream API、函数式接口、方法引用
JDK1.8新特性(一) ----Lambda表达式、Stream API、函数式接口、方法引用
JDK1.8新特性(一) ----Lambda表达式、Stream API、函数式接口、方法引用
 JDK1.8新特性(一) ----Lambda表达式、Stream API、函数式接口、方法引用
下边是Lmabda表达式的语法总结:
JDK1.8新特性(一) ----Lambda表达式、Stream API、函数式接口、方法引用 
JDK1.8新特性(一) ----Lambda表达式、Stream API、函数式接口、方法引用
口诀:左右遇一省括号,左侧推断类型省
 
注:当一个接口中存在多个抽象方法时,如果使用lambda表达式,并不能智能匹配对应的抽象方法,因此引入了函数式接口的概念。
 
先介绍一下Stream API,上边提到了Stream API,挨着写,下一个介绍函数式接口。
Stream API:
    Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简单来说,Stream API 提供了一种高效且易于使用的处理数据的方式。
     注意:1.Stream不会自己存储元素
               2.Stream不会改变源对象,相反他们会返回一个持有新结果集的Stream对象。
               3.Stream的操作是延迟执行的,这意味着他们会等到需要结果的时候才执行
               4.  只有当作终止操作时,所有的中间操作会一次性的全部执行,称为“惰性求值”、“延迟加载”  
               5. 每一个Stream流只能操作一次,如果第二次操作会报错stream has already been operated upon or closed
 
    Stream API的执行流程:
JDK1.8新特性(一) ----Lambda表达式、Stream API、函数式接口、方法引用
JDK1.8新特性(一) ----Lambda表达式、Stream API、函数式接口、方法引用
直接上代码:
    1.创建流Stream
JDK1.8新特性(一) ----Lambda表达式、Stream API、函数式接口、方法引用
JDK1.8新特性(一) ----Lambda表达式、Stream API、函数式接口、方法引用
    2.中间操作
JDK1.8新特性(一) ----Lambda表达式、Stream API、函数式接口、方法引用
JDK1.8新特性(一) ----Lambda表达式、Stream API、函数式接口、方法引用
    3.终止操作
 //3.终止操作
/**
* 查找和匹配
* allMatch-检查是否匹配所有元素
* anyMatch-检查是否至少匹配一个元素
* noneMatch-检查是否没有匹配所有元素
* findFirst-返回第一个元素
* findAny-返回当前流中的任意元素
* count-返回流中元素的总个数
* max-返回流中最大值
* min-返回流中最小值
*/
//3.1:allMatch检查是否匹配所有元素是否都成立,都成立返回true 否则返回false
Stream<Map<String, Object>> stream2 = maps.stream();
boolean a = stream2.allMatch(x -> Integer.valueOf(x.get("age").toString()) > 16);
System.err.println("结果:"+a); //false
//3.2:anyMatch检查是否至少匹配一个元素,只要有一个成立就返回true
Stream<Map<String, Object>> stream3 = maps.stream();
boolean b = stream3.anyMatch(x -> Integer.valueOf(x.get("age").toString()) > 16);
System.err.println("结果:"+b); //true
//3.3:noneMatch检查是否没有匹配所有元素,因为有成立的所以有匹配的元素,估 不成立
Stream<Map<String, Object>> stream4 = maps.stream();
boolean c = stream4.noneMatch(x -> Integer.valueOf(x.get("age").toString()) > 16);
System.err.println("结果:"+c); //false
//3.4:findFirst返回第一个元素,按照年龄从小到大排序返回第一个元素
Stream<Map<String, Object>> stream5 = maps.stream();
Map<String, Object> first = stream5.sorted((x,y) -> Integer.compare(Integer.valueOf(x.get("age")
.toString()),Integer.valueOf(y.get("age").toString()))).findFirst().get();
System.err.println(first.toString());//{sex=女, name=小红, age=16}
//3.5:findAny-返回当前流中的任意元素
Stream<Map<String, Object>> stream6 = maps.stream();
Map<String, Object> map = stream6.sorted((x,y) -> Integer.compare(Integer.valueOf(y.get("age")
.toString()),Integer.valueOf(x.get("age").toString()))).findAny().get();
//多次测试返回固定是这个,感觉因该是内部有一个算法排序然后返回其中固定某个 {sex=男, name=小明, age=18}
//排序之后返回的永远是第一个
System.err.println(map.toString());
//3.6:返回流中元素的总个数
Stream<Map<String, Object>> stream7 = maps.stream();
long count = stream7.count();
System.err.println("长度为:"+count); // 长度为:3
//TODO 最大最小就不测试了,自己可以试试

还有功能比较强大的两个终止操作 reduce和collect

reduce操作: reduce:(T identity,BinaryOperator)/reduce(BinaryOperator)-可以将流中元素反复结合起来,得到一个值
 /**
* reduce :规约操作
* 计算和
* 计算结果作为x,再从数组中取出新值作为y,进行下一步计算
* 结果在加上0 是最后的结果
*/
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer count2 = list.stream().reduce(0, (x, y) -> x + y);
System.out.println(count2); Stream<Map<String, Object>> stream8 = maps.stream();
//计算总年龄 也可以是浮点型数据 将Integer 换成Double就ok
Optional<Integer> op = stream8.map(m -> Integer.valueOf(String.valueOf(m.get("age"))))
.reduce(Integer::sum);
System.err.println(op.get()); //    
collect操作:Collect-将流转换为其他形式,接收一个Collection接口的实现,用于给Stream中元素做汇总的方法,转换为一个新的集合或者对象
 /**
* collect操作:Collect-将流转换为其他形式,
* 接收一个Collection接口的实现,用于给Stream中元素做汇总的方法
* Collectors.toList()/toSet()
*/
Set<Integer> ageList = list.stream().collect(Collectors.toSet());
ageList.stream().forEach(System.out::println);
//取名字,封装成hashset
HashSet<Integer> hs = list.stream()
.collect(Collectors.toCollection(HashSet::new));
System.err.println(hs.toString());

函数式接口:

        函数式接口的提出是为了给Lambda表达式的使用提供更好的支持。
什么是函数式接口?
        简单来说就是只定义了一个抽象方法的接口(Object类的public方法除外),就是函数式接口,并且还提供了注解:@FunctionalInterface
函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
函数式接口可以被隐式转换为 lambda 表达式。
Lambda 表达式和方法引用(实际上也可认为是Lambda表达式)上。
如定义了一个函数式接口如下:
 @FunctionalInterfaceinterface GreetingService
{
void sayMessage(String message);
} // 那么就可以使用Lambda表达式来表示该接口的一个实现(注:JAVA 8 之前一般是用匿名类实现的):
GreetingService greetService1 = message -> System.out.println("Hello " + message);
常见的四大函数式接口有:
        Consumer 《T》:消费型接口,有参无返回值
        Supplier 《T》:供给型接口,无参有返回值
        Function 《T,R》:函数式接口,有参有返回值
        Predicate《T》:断言型接口,有参有返回值,返回值是boolean类型
 /**
* @MethodName: functionInterface
* @Description: 四大函数式接口练习
* @param
* @return
* @author rongrong
* @date 2019-12-23
*/
public void functionInterface(){
/**
* 1.Consumer 《T》:消费型接口,有参无返回值
* 打印:
* 222222
* hello
*/
changeStr("hello",(str) -> System.err.println(str)); /**
* 2.Supplier 《T》:供给型接口,无参有返回值
* 打印:
* 111111
* str
*/
String value = getValue(() -> "str");
System.err.println(value); /**
* 3. Function 《T,R》::函数式接口,有参有返回值
* 打印:
* 333333
* 20000
*/
Long aLong = changeNum(100L, x -> x * 200);
System.err.println(aLong);
/**
* Predicate《T》: 断言型接口,有参有返回值,返回值是boolean类型
* 打印:
* true
*/
boolean rongrong = changeBoolean("rongrong", x -> x.equals("rongrong"));
System.err.println(rongrong); } /**
* Consumer<T> 消费型接口
* @param str
* @param con
*/
public void changeStr(String str, Consumer<String> con){
System.err.println("222222");
con.accept(str);
} /**
* Supplier<T> 供给型接口
* @param sup
* @return
*/
public String getValue(Supplier<String> sup){
System.err.println("111111");
return sup.get();
} /**
* Function<T,R> 函数式接口
* @param num
* @param fun
* @return
*/
public Long changeNum(Long num, Function<Long, Long> fun){
System.err.println("333333");
return fun.apply(num);
} /**
* Predicate<T> 断言型接口
* @param str
* @param pre
* @return
*/
public boolean changeBoolean(String str, Predicate<String> pre){
return pre.test(str);
}

在四大核心函数式接口基础上,还提供了诸如BiFunction、BinaryOperation、toIntFunction等扩展的函数式接口,都是在这四种函数式接口上扩展而来的,不做赘述。

总结:函数式接口的提出是为了让我们更加方便的使用lambda表达式,不需要自己再手动创建一个函数式接口,直接拿来用就好了,详细接口描述在下边。
 
 
方法引用:
        若lambda体中的内容有方法已经实现了,那么可以使用“方法引用”也可以理解为方法引用是lambda表达式的另外一种表现形式并且其语法比lambda表达式更加简单
(a) 方法引用
    三种表现形式:
        1. 对象:实例方法名
        2. 类::静态方法名
        3. 类::实例方法名 (lambda参数列表中第一个参数是实例方法的调用 者,第二个参数是实例方法的参数时可用)
 /**
*注意:
* 1.lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致!
* 2.若lambda参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用ClassName::method
* 下边写的例子 上边是不简化时的写法,下边的是对应的简化写法,就是方法引用
*/ //100
//
Consumer<Integer> con = (x) -> System.out.println(x);
con.accept(100);
// 方法引用-对象::实例方法
Consumer<Integer> con2 = System.out::println;
con2.accept(200); // 方法引用-类名::静态方法名
BiFunction<Integer, Integer, Integer> biFun = (x, y) -> Integer.compare(x, y);
Integer apply = biFun.apply(100, 200);
BiFunction<Integer, Integer, Integer> biFun2 = Integer::compare;
Integer result = biFun2.apply(100, 200);
//-1:-1
System.err.println(apply + ":" + result); // 方法引用-类名::实例方法名
BiFunction<String, String, Boolean> fun1 = (str1, str2) -> str1.equals(str2);
Boolean apply1 = fun1.apply("rong", "rong");
BiFunction<String, String, Boolean> fun2 = String::equals;
Boolean result2 = fun2.apply("hello", "world");
//true:false
System.out.println(apply1 + ":" + result2);
(b)构造器引用 
        格式:ClassName::new
 // 构造方法引用 类名::new
Supplier<String> sup = () -> new String();
System.out.println(sup.get());
Supplier<String> sup2 = String::new;
System.out.println(sup2.get()); // 构造方法引用 类名::new (带一个参数)
//x 代表是传进去的参数,也就是泛型中的Integer类型
//new String(...) 代表泛型中的String类型
Function<Integer, String> fun = x -> new String(String.valueOf(x));
System.err.println(fun.apply(100));
Function<String, String> fun3 = String::new;
System.out.println(fun3.apply("100"));
(c)数组引用
格式:Type[]::new
 // 数组引用
Function<Integer, String[]> arrayFun = (x) -> new String[x];
Function<Integer, String[]> arrayFun2 = String[]::new;
//给String数组设置了两个长度。但是值是null
String[] strArray = arrayFun2.apply(2);
Arrays.stream(strArray).forEach(System.out::println);
 
序号
接口 & 描述
1
BiConsumer<T,U>
代表了一个接受两个输入参数的操作,并且不返回任何结果
2
BiFunction<T,U,R>
代表了一个接受两个输入参数的方法,并且返回一个结果
3
BinaryOperator<T>
代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果
4
BiPredicate<T,U>
代表了一个两个参数的boolean值方法
5
BooleanSupplier
代表了boolean值结果的提供方
6
Consumer<T>
代表了接受一个输入参数并且无返回的操作
7
DoubleBinaryOperator
代表了作用于两个double值操作符的操作,并且返回了一个double值的结果。
8
DoubleConsumer
代表一个接受double值参数的操作,并且不返回结果。
9
DoubleFunction<R>
代表接受一个double值参数的方法,并且返回结果
10
DoublePredicate
代表一个拥有double值参数的boolean值方法
11
DoubleSupplier
代表一个double值结构的提供方
12
DoubleToIntFunction
接受一个double类型输入,返回一个int类型结果。
13
DoubleToLongFunction
接受一个double类型输入,返回一个long类型结果
14
DoubleUnaryOperator
接受一个参数同为类型double,返回值类型也为double 。
15
Function<T,R>
接受一个输入参数,返回一个结果。
16
IntBinaryOperator
接受两个参数同为类型int,返回值类型也为int 。
17
IntConsumer
接受一个int类型的输入参数,无返回值 。
18
IntFunction<R>
接受一个int类型输入参数,返回一个结果 。
19
IntPredicate
:接受一个int输入参数,返回一个布尔值的结果。
20
IntSupplier
无参数,返回一个int类型结果。
21
IntToDoubleFunction
接受一个int类型输入,返回一个double类型结果 。
22
IntToLongFunction
接受一个int类型输入,返回一个long类型结果。
23
IntUnaryOperator
接受一个参数同为类型int,返回值类型也为int 。
24
LongBinaryOperator
接受两个参数同为类型long,返回值类型也为long。
25
LongConsumer
接受一个long类型的输入参数,无返回值。
26
LongFunction<R>
接受一个long类型输入参数,返回一个结果。
27
LongPredicate
R接受一个long输入参数,返回一个布尔值类型结果。
28
LongSupplier
无参数,返回一个结果long类型的值。
29
LongToDoubleFunction
接受一个long类型输入,返回一个double类型结果。
30
LongToIntFunction
接受一个long类型输入,返回一个int类型结果。
31
LongUnaryOperator
接受一个参数同为类型long,返回值类型也为long。
32
ObjDoubleConsumer<T>
接受一个object类型和一个double类型的输入参数,无返回值。
33
ObjIntConsumer<T>
接受一个object类型和一个int类型的输入参数,无返回值。
34
ObjLongConsumer<T>
接受一个object类型和一个long类型的输入参数,无返回值。
35
Predicate<T>
接受一个输入参数,返回一个布尔值结果。
36
Supplier<T>
无参数,返回一个结果。
37
ToDoubleBiFunction<T,U>
接受两个输入参数,返回一个double类型结果
38
ToDoubleFunction<T>
接受一个输入参数,返回一个double类型结果
39
ToIntBiFunction<T,U>
接受两个输入参数,返回一个int类型结果。
40
ToIntFunction<T>
接受一个输入参数,返回一个int类型结果。
41
ToLongBiFunction<T,U>
接受两个输入参数,返回一个long类型结果。
42
ToLongFunction<T>
接受一个输入参数,返回一个long类型结果。
43
UnaryOperator<T>
接受一个参数为类型T,返回值类型也为T。