一、 Lambda表达式
- 举例:
(o1, o2) -> Integer.compare(o1,o2); - 格式:
-> :Lambda操作符 或 箭头操作符 -> 左边:Lambda形参列表,就是接口中的抽象方法的形参列表 -> 右边:Lambda体,即重写的抽象方法的方法体 Lambda表达式的本质:作为函数式接口的实例 :所有函数式接口的匿名实现类都可以用Lambda表示
1.1 函数式接口
如果一个接口种只声明了一个抽象方法 ,则此方法称为函数式接口
1.1.1 Java内置四大核心函数式接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|
消费型接口:Consumer | T | void | 对类型为T的对象应用操作,包含方法:void accept(T t) | 供给型接口:Supplier | 无 | T | 返回类型为T的对象,包含方法:T get() | 函数型接口:Function<T,R> | T | R | 对类型为T的对象应用操作,并返回结果。结果时R类型的对象。包含方法:R apply(T t) | 断定型接口 Predicate | T | boolean | 确定类型为T的对象是否满足某约束,并返回boolean值。包含方法:boolean test(T t) |
1.1.2 Lambda表达式使用举例
例一:
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("我爱北京天安门");
}
};
上述代码等价于:
Runnable r2 = () -> System.out.println("我爱北京天安门");
例二:
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1,o2);
}
};
Lambda表达式写法;
Comparator<Integer> com2 = (o1, o2) -> Integer.compare(o1,o2);
方法引用:
Comparator<Integer> com3 = Integer::compare;
1.2 Lambda表达式使用的六种情况
1.2.1 无参、无返回值
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("我爱北京天安门");
}
};
Runnable r2 = () -> System.out.println("我爱北京天安门");
1.2.2 有一个参数,但无返回值
Consumer<String> con = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
con.accept("normal print");
Consumer<String> con1 = (String s1) -> {
System.out.println(s1);
};
con1.accept("lambda print");
1.2.3 数据类型可以省略,因为可以由编译器推断得出,称为“类型推断”
1.2.2中的Lambda表达式还可以写成:
Consumer<String> con2 = (s1) -> {
System.out.println(s1);
};
con1.accept("lambda print");
1.2.4 Lambda 若只需要一个参数时,参数的小括号可以省略
即 1.2.3中的表达式可以继续写为
Consumer<String> con2 = s1 -> {
System.out.println(s1);
};
con1.accept("lambda print");
1.2.5 Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
}
};
Comparator<Integer> com2 = (o1, o2) -> {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
1.2.6 Lambda体只有一条语句时,return与大括号若有,都可以省略
Comparator<Integer> com3 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
};
Comparator<Integer> com4 = (o1, o2) -> o1.compareTo(o2);
1.3 方法引用和构造器引用
1.3.1 方法引用
当要传递给Lambda体的操作(重写的方法),已经有实现的方法了,就可以使用方法引用 - 方法引用是Lambda表达式深层次的表达。方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方向,可以认为是Lambda表达式的一个语法糖。
- 要求:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值保持一致(针对情况一和情况二)
- 格式:使用操作符
:: 将类(或对象)与方法名分隔开 - 三种主要使用情况
情况一:对象::非静态方法名 情况二:类::静态方法名 情况三:(难点)类::非静态方法名
1.3.1.1 方法引用示例
① 对象 :: 实例方法
@Test
public void test1(){
Consumer<String> con1 = str -> System.out.println(str);
con1.accept("beijing");
PrintStream ps = System.out;
Consumer<String> con2 = ps :: println;
con2.accept("北京");
}
@Test
public void test2(){
Employee emp = new Employee(1001,"Tom",12);
Supplier<String> sup1 = () -> emp.getName();
System.out.println(sup1.get());
Supplier<String> sup2 = emp :: getName;
}
②类 :: 静态方法
@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;
}
@Test
public void test4() {
Function<Double, Long> func1 = d -> Math.round(d);
func1.apply(2.3);
Function<Double, Long> func2 = Math::round;
func1.apply(5.4);
}
③ 类 :: 实例方法 (难点)
@Test
public void test5(){
Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2);
Comparator<String> com2 = String :: compareTo;
}
@Test
public void test6(){
BiPredicate<String,String> pre1 = (s1,s2) -> s1.equals(s2);
BiPredicate<String,String> pre2 = String :: equals;
}
@Test
public void test7(){
Function<Employee,String> func = new Function<Employee, String>() {
@Override
public String apply(Employee employee) {
return null;
}
};
Employee employee = new Employee(1003, "Alice", 24);
Function<Employee,String> func1 = e -> e.getName();
func1.apply(employee);
Function<Employee,String> func2 = Employee :: getName;
}
1.3.2 构造器引用
和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一样 抽象方法的返回值类型即为构造器所属的类
@Test
public void test1(){
Supplier<Employee> supplier = new Supplier<Employee>() {
@Override
public Employee get() {
return null;
}
} ;
Supplier<Employee> sup1 = () -> new Employee();
Supplier<Employee> sup2 = Employee::new;
}
@Test
public void test2(){
Function<Integer,Employee> func1 = id -> new Employee(id);
Employee employee = func1.apply(1001);
Function<Integer,Employee> func2 = Employee :: new;
Employee employee1 = func2.apply(1002);
}
@Test
public void test3(){
BiFunction<Integer,String,Employee> func1 = (id,name) -> new Employee(id,name);
func1.apply(1001,"Tom");
BiFunction<Integer,String,Employee> func2 = Employee :: new;
}
1.3.3 数组引用
可以把数组看作是一个特殊的类,数组引用的方法就和构造引用一样了
@Test
public void test4(){
Function<Integer,String[]> func1 = length -> new String[length];
String[] arr1 = func1.apply(5);
Arrays.toString(arr1);
Function<Integer,String[]> func2 = String[] :: new;
String[] arr2 = func2.apply(10);
Arrays.toString(arr2);
}
二、StreamAPI
Stream API 可以对集合数据进行类似于SAL执行的数据库查询。也可以使用Stream API 来并行执行操作,建简而言之,Stream API提供了一种高效且易于使用的处理数据的方式
2.1 为什么要使用Stream API
- 实际开发中,项目中多数数据源都来自Mysql,Oracle关系型数据库等。但现在数据源可以更多了,有MongDB,Radis等,而这些NoSQL(非关系型数据库)的数据就需要Java层面去处理。
- Stream 和Collection集合的区别:
Collection是一种静态的内存数据结构,而Stream是有关计算的 。前者主要面向内存,存储在内存中,后者主要是面向CPU,通过CPU实现计算
Stream是什么
- Stream是数据渠道,用于操作数据源就(集合、数组等)所生产的元素序列
集合讲的是数据,Stream讲的是计算
注意项
① Stream 自己不会存储元素 ② Stream 不会改变源对象。相反,他们会返回一个持有结果的新Steam ③ Stream 操作是延迟执行的。这意味着他们会等到需要结果时才执行。
2.2 Stream的操作的三个步骤
2.2.1、创建 Stream
一个数据源(如:集合、数组),获取一个流
通过集合创建Stream
Java8的Collection接口被扩展,提供了两个获取流的方法
default Stream<E> stream() :返回一个顺序流 default Stream<E> parallelStream() :返回一个并行流
List<Employee> employees = EmployeeDate.getEmployees();
Stream<Employee> stream = employees.stream();
Stream<Employee> employeeStream = employees.parallelStream();
通过数组创建Stream
Java8 中的Arrays的静态方法 stream()可以获取数组流 static <T> Stream<T> stream(T[] array):返回一个流
int [] arr = new int[]{1,2,3,4,5,6};
IntStream stream1 = Arrays.stream(arr);
Employee e1 = new Employee(1001,"Tom");
Employee e2 = new Employee(1002,"Bob");
Employee e3 = new Employee(1003,"Siri");
Employee e4 = new Employee(1004,"Alix");
Employee[] ees = new Employee[]{e1,e2,e3,e4};
Stream<Employee> stream2 = Arrays.stream(ees);
通过Stream的of()
调用Stream类静态方法of(),通过显示值创建一个流。它可以接收任意数量的参数。
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6);
创建无限流
Stream.iterate(0,t->t+2).limit(10).forEach(System.out::println);
Stream.generate(Math::random).limit(10).forEach(System.out::println);
2.2.2、中间操作
一个中间操作链,对数据源的数据进行处理,只有在需要结果的时候才会去执行
筛选与切片
@Test
public void test1(){
List<Employee> employees = EmployeeDate.getEmployees();
Stream<Employee> stream = employees.stream();
stream.filter(e -> e.getAge() < 23).forEach(System.out::println);
System.out.println();
employees.stream().limit(3).forEach(System.out::println);
System.out.println();
employees.stream().skip(3).forEach(System.out::println);
System.out.println();
System.out.println(employees);
employees.stream().distinct().forEach(System.out::println);
}
输出:
Employee{id=1002, name='刘兰', age=19}
Employee{id=1007, name='郑兰', age=22}
Employee{id=1007, name='郑兰', age=22}
Employee{id=1001, name='周兰', age=23}
Employee{id=1002, name='刘兰', age=19}
Employee{id=1003, name='张兰', age=28}
Employee{id=1004, name='李兰', age=23}
Employee{id=1005, name='王兰', age=26}
Employee{id=1006, name='杨兰', age=24}
Employee{id=1007, name='郑兰', age=22}
Employee{id=1005, name='王兰', age=26}
Employee{id=1006, name='杨兰', age=24}
Employee{id=1007, name='郑兰', age=22}
[Employee{id=1001, name='周兰', age=23}, Employee{id=1002, name='刘兰', age=19}, Employee{id=1003, name='张兰', age=28}, Employee{id=1004, name='李兰', age=23}, Employee{id=1005, name='王兰', age=26}, Employee{id=1006, name='杨兰', age=24}, Employee{id=1007, name='郑兰', age=22}, Employee{id=1005, name='王兰', age=26}, Employee{id=1006, name='杨兰', age=24}, Employee{id=1007, name='郑兰', age=22}]
Employee{id=1001, name='周兰', age=23}
Employee{id=1002, name='刘兰', age=19}
Employee{id=1003, name='张兰', age=28}
Employee{id=1004, name='李兰', age=23}
Employee{id=1005, name='王兰', age=26}
Employee{id=1006, name='杨兰', age=24}
Employee{id=1007, name='郑兰', age=22}
映射
@Test
public void test2(){
List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
list.stream().map(String::toUpperCase).forEach(System.out::println);
List<Employee> employees = EmployeeDate.getEmployees();
Stream<String> namesStream = employees.stream().map(Employee::getName);
namesStream.filter(name -> name.length() > 3).forEach(System.out::println);
Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest1::stringToStream);
streamStream.forEach(s->{
s.forEach(System.out::println);
});
System.out.println();
Stream<Character> characterStream = list.stream().flatMap(StreamAPITest1::stringToStream);
characterStream.forEach(System.out::println);
}
stringToStream(String s)方法:
public static Stream<Character> stringToStream(String s){
ArrayList<Character> list = new ArrayList<>();
for(Character c : s.toCharArray()){
list.add(c);
}
return list.stream();
}
排序
@Test
public void test(){
List<Integer> li = Arrays.asList(12, 65, 78, 1, 23, 3, 0, 8, -12);
li.stream().sorted().forEach(System.out::println);
List<Employee> employees = EmployeeDate.getEmployees();
employees.stream().sorted((e1,e2) -> {
int ageValue = Integer.compare(e1.getAge(),e2.getAge());
if (ageValue != 0){
return ageValue;
}else {
return e1.getName().compareTo(e2.getName());
}
}).forEach(System.out::println);
}
2.2.3、终止操作(终端操作)
一旦执行终止操作,就执行中间操作链 ,并产生结果。之后不可以再被使用
匹配与查找
@Test
public void test1(){
List<Employee> employees = EmployeeDate.getEmployees();
boolean b = employees.stream().allMatch(e -> e.getAge() > 18);
System.out.println(b);
Optional<Employee> first = employees.stream().findFirst();
Optional<Employee> any = employees.stream().findAny();
System.out.println(first);
System.out.println(any);
long count = employees.stream().count();
System.out.println(count);
Stream<Integer> ages = employees.stream().map(e -> e.getAge());
Optional<Integer> max = ages.max(Double::compare);
System.out.println(max);
Optional<Employee> min = employees.stream().min((e1, e2) -> Double.compare(e1.getAge(), e2.getAge()));
System.out.println(min);
employees.stream().forEach(System.out::println);
}
归约
@Test
public void test2() {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = list.stream().reduce(0, Integer::sum);
System.out.println(sum);
List<Employee> employees = EmployeeDate.getEmployees();
Stream<Integer> agesStream = employees.stream().map(Employee::getAge);
Optional<Integer> sumAge = agesStream.reduce(Integer::sum);
System.out.println(sumAge);
}
收集
collect (Collector c) 将流转换为其他形式,接收一个Collector接口的实现,用于给Steam中元素做汇总的方法 Collector接口中方法的实现决定了,如何对流执行收集的操作(如收集到List、Set、Map)
@Test
public void test3(){
List<Employee> employees = EmployeeDate.getEmployees();
List<Employee> employeeList = employees.stream().filter(e -> e.getAge() > 20).collect(Collectors.toList());
employeeList.forEach(System.out::println);
Set<Employee> employeeSet = employees.stream().filter(e -> e.getAge() > 20).collect(Collectors.toSet());
employeeSet.forEach(System.out::println);
}
三、Optional类
- 来源:为了解决空指针异常,Google公司著名的Guava项目映入Optional类,Guava通过使用检查空值的方式来防止代码污染。收到Google Guava的启发,Optional类已经成为Java8类库的一部分
- Opyional类(java.util.Optional)是一个
容器类 ,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。原来用null,表示一个值不存在,现在Optional可以更好的表达这个概念。并且可以更好的避免空指针异常 . - Potional类的Javadoc描述如下:这是一个可以为unll的容器对象,如果值存在则isPresent()方法返回true,调用get()方法会返回该对象。
3.1 Optional类中的方法
Optional类提供了很多方法,使得我们不用显式地进行空值检测
3.1.1 创建Optional类对象的方法
Optional.of(T t) 创建一个Optional实例,t必须非空
Girl girl= new Girl();
Optional<Girl> girl1 = Optional.of(girl);
System.out.println("************");
girl = null;
Optional<Girl> girl2 = Optional.of(girl);
输出:
************
java.lang.NullPointerException
at java.util.Objects.requireNonNull(Objects.java:203)
at java.util.Optional.<init>(Optional.java:96)
at java.util.Optional.of(Optional.java:108)
由结果可知,第一次实例化对象成功,第二次空指针异常
Optional.empty():创建一个空的 Optional实例
Optional.ofNullable(T t): t可以为null
@Test
public void test3(){
Girl girl= new Girl();
Optional<Girl> girl1 = Optional.ofNullable(girl);
System.out.println(girl1);
Optional<Girl> girl2 = Optional.ofNullable(null);
System.out.println(girl2);
}
输出
Optional[Girl{name='null', age=0}]
Optional.empty
3.1.2 获取Optional容器对象
T orElse(T other):
如果有值则将其返回,否则返回指定的other对象
Girl girl = new Girl();
Optional<Girl> girl1 = Optional.ofNullable(girl);
Optional<Girl> girl2 = Optional.ofNullable(null);
Girl girl3 = girl1.orElse(new Girl("刘亦菲", 30));
Girl girl4 = girl2.orElse(new Girl("刘亦菲", 30));
System.out.println(girl3);
System.out.println(girl4);
输出
Girl{name='null', age=0}
Girl{name='刘亦菲', age=30}
ofNullable + orElse的一个综合使用
getGirlName类:
public String getGirlName(Boy boy) {
Optional<Boy> boyOptional = Optional.ofNullable(boy);
Boy boy1 = boyOptional.orElse(new Boy(new Girl("杨幂", 31)));
Girl girl = boy1.getGirl();
Optional<Girl> girlOptional = Optional.ofNullable(girl);
Girl girl1 = girlOptional.orElse(new Girl("迪丽热巴", 30));
return girl1.getName();
}
测试:
@Test
public void test4(){
Boy boy = null;
String girlName = getGirlName(boy);
System.out.println(girlName);
Boy boy1 = new Boy();
String girlName1 = getGirlName(boy1);
System.out.println(girlName1);
Boy boy2 = new Boy(new Girl());
String girlName2 = getGirlName(boy2);
System.out.println(girlName2);
Boy boy3 = new Boy(new Girl("初恋女友",18));
String girlName3 = getGirlName(boy3);
System.out.println(girlName3);
}
输出:
杨幂
迪丽热巴
null
初恋女友
|