尚硅谷_Java零基础教程-java入门必备-初学者基从入门到精通全套完整版(宋红康主讲) P666-665
文章目录
1. Lambda表达式
lambda表达式的使用:6种语法格式
- 无参无返回值
Runnable r = ()->System.out.println("lambda Runnable");
- 有参无返回值
Consumer<String> con = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
con.accept("accept");
Consumer<String> con1 = (String s)->{
System.out.println(s);
};
con1.accept("accept lambda");
- 类型推断:数据类型可以省略,因为可由编译器推断得出
Consumer<String> con1 = (s) -> System.out.println(s);
- 若只有一个参数,参数的小括号可以省略。
Consumer<String> con1 = s -> System.out.println(s);
- 有参有返回值:两个或以上的参数,多条执行语句,并且可以有返回值
Comparator<Integer> com = (o1,o2)-> {
System.out.println(o1);
System.out.println(o2);
return Integer.compare(o1,o2);
};
- 只有一条语句时,大括号可以省略
Comparator<Integer> com = (o1,o2)-> Integer.compare(o1,o2);
总结
- 左边:
- 形参列表的参数类型可以省略(参数推断)
- 如果只有一个参数,则小括号可以省略
- 右边:
- 如果只有一条执行语句,则大括号可以省略。
- 若是return语句,则return关键字省略
- 本质:
- 作为函数式接口(
@FunctionalInterface
)的实现(只有一个抽象方法)
- 作为函数式接口(
2. 函数式接口
- 只包含一个抽象方法的接口
- 可以通过Lambda表达式创建接口的对象
- 可以在一个接口上使用
@FunctionalInterface
注解,可以检查它是否是一个函数式接口。javadoc也会包含一条声明 - 在java.util.function包下定义了java8的丰富的函数式接口
如何理解函数式接口
- java里面向对象OOP是一切。但是随着python、scala等语言的兴起和新技术的挑战,Java不得不做出调整以便支持更加广泛的技术要求,也即java不但可以支持OOP还可以支持OOF面向函数编程
- 在函数式编程语言中,函数被当作”一等公民“对待。在函数式编程语言中,Lambda表达式的类型是函数。但是在java8中,有所不同,lamda表达式是对象,而不是函数。它们必须依附于一类特别的对象类型——函数式接口。
- 简单来说,在java8中,lambda表达式就是一个函数式接口的实例。这就是lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口的实例,那么就可以用lambda表达式来标识。
- 所以,以前用匿名实现类表示的现在都可以用lambda表达式来写。
java内置四大核心函数式接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer <T> 消费型接口 | T | void | 对类型为T的对象应用操作,包含方法:void accept(T t) |
Supplier<T> 供给型接口 | 无 | T | 返回类型为T的对象,包含方法:T get() |
Function<T,R> 函数型接口 | T | R | 对类型为T的对象应用操作,并返回结果,结果是R类型的对象。包含方法:R apply(T t) |
Predicate<T> 断定型接口 | T | boolean | 确定类型为T的对象是否满足某约束,包含方法:boolean test(T t) |
测试 Consumer <T>
:
@Test
public void test1(){
happyTime(500, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println("use some money:"+aDouble);
}
});
System.out.println("*************************");
happyTime(400,money -> System.out.println("some money"+money));
}
public void happyTime(double money ,Consumer<Double> con){
con.accept(money);
}
测试 Predicate<T>
:
@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("瓜");
}
});
System.out.println(filterStrs);
System.out.println("*************************");
final List<String> list1 = filterString(list, s -> s.contains("瓜"));
System.out.println(list1);
}
//根据给定的规则,过滤集合中的字符串
public List<String> filterString(List<String> list, Predicate<String > pre){
ArrayList<String> filterList = new ArrayList<>();
for (String s:list ) {
if (pre.test(s)){
filterList.add(s);
}
}
return filterList;
}
3. 方法引用与构造器引用
方法引用的使用
- 使用情景:当要传递给lamda体的操作,已经有是心啊的方法了,可以使用方法引用
- 方法引用:本质上就是lambda表达式。方法引用,也是函数式接口的实例
- 使用格式:类(或对象)::方法名
- 三种情况
对象::实例方法
类::静态方法
类::非静态方法 - 方法引用使用的要求:接口中的抽象方法的形参列表和返回值类型与方法引用的方法完全相同(情况1和情况2)
情况一 对象 :: 实例方法
//情况一 对象::实例方法 --1
//Consumer的void accept(T t)
//PrintStream中的void println(T t)
@Test
public void test1(){
Consumer<String> con1 = str-> System.out.println(str);
con1.accept("Beijing");
System.out.println("*****************To方法引用****************");
PrintStream ps = System.out;
Consumer<String> con2 = ps::println;
con2.accept("Beijing again");
}
//情况一 对象::实例 --2
//Supplier 的void get()
//Employee 中的getName()
@Test
public void test2(){
Employee tom = new Employee(2001, "Tom", 23, 5600);
Supplier<String> sup1 = ()->tom.getName();
System.out.println(sup1.get());
System.out.println("*****************To方法引用****************");
Supplier<String> sup2 = tom::getName;
System.out.println(sup2.get());
}
情况二 类 :: 静态方法
//Comparator 的 int compare(T t1, T t2)
//Integer 中的 int compare(T t1, T t2)
@Test
public void test3(){
Comparator<Integer> con1 = (t1,t2)->Integer.compare(t1,t2);
System.out.println(con1.compare(12, 34));
System.out.println("*****************To方法引用****************");
Comparator<Integer> con2 = Integer::compare;
System.out.println(con2.compare(12, 34));
}
//Function 中的 R apply(T t)
//Math 中的 Long round (Double d)
@Test
public void test4(){
Function<Double,Long> fun1 = t-> Math.round(t);
System.out.println(fun1.apply(2.4));//2
System.out.println("*****************To方法引用****************");
Function<Double,Long> fun2 = Math::round;
System.out.println(fun2.apply(2.5));//3
}
情况三 类 :: 非静态方法
//情况三 类 :: 实例方法
//Comparator 的 int compare(T t1, T t2)
//String 中的 int t1.compareTo( t2)
@Test
public void test5(){
Comparator<String> con1 = (t1,t2)->t1.compareTo(t2);
System.out.println(con1.compare("12", "34"));
System.out.println("*****************To方法引用****************");
Comparator<String> con2 = String :: compareTo;
System.out.println(con2.compare("12", "34"));
}
//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("abc", "abc"));
System.out.println("*****************To方法引用****************");
BiPredicate<String,String> pre2 = String::equals;
System.out.println(pre2.test("abc", "ABC"));
}
构造器引用
无参构造器
//Supplier : T get()
@Test
public void test1(){
Supplier<Employee> sup = new Supplier<Employee>() {
@Override
public Employee get() {
return new Employee();
}
};
System.out.println(sup.get());
System.out.println("*********To 构造器引用******");
Supplier<Employee> sup2 = Employee::new;
System.out.println(sup2.get());
}
含参构造器
//Function : R apply(T t)
@Test
public void test2(){
Function<Integer,Employee> fun1 = id->new Employee(id);
System.out.println(fun1.apply(30));
System.out.println("*********To 构造器引用******");
Function<Integer,Employee> fun2 = Employee::new;
System.out.println(fun2.apply(35));
}
//BiFunction
@Test
public void test3(){
BiFunction<Integer,String,Employee> bif1 = (id,name)->new Employee(id,name);
BiFunction<Integer,String,Employee> bif2 = Employee::new;
System.out.println(bif1.apply(1001, "fun") + "/" + bif2.apply(1002, "new"));
}
数组 String[]::new
//数组引用
//Function : R apply(T t)
@Test
public void test4(){
Function<Integer,String[]> func1 = len->new String[len];
Function<Integer,String[]> func2 = String[]::new;
System.out.println(func1.apply(2));
System.out.println("/");
System.out.println(func2.apply(3));
}
4. Stream API
- java8中两大重要改变:Lambda表达式和Stream API
- Stream API(java.util.stream)把真正的函数式编程风格引入到java中。这是目前为止对java类库最好的补充,因为Stream API可以极大的提供java程序员的生产力,让程序员写出高效率、干净、简洁的代码
- Stream 是java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API对集合数据进行操作,就类似于使用SQL执行的数据库查询。也可以使用Stream API来并行执行操作。简言之,Stream API提供了一种高效且易于使用的处理数据的方式。
Stream特点
- Stream关注的是对数据的运算,与CPU打交道
- 集合关注的是数据的存储,与内存打交道
- Stream自己不会存储元素
- Stream不会改变源对象
- Stream操作延迟执行
Stream操作三个步骤
- 创建:一个数据源,获取一个流
- 中间操作:对数据源的数据进行处理
- 终止操作:一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用
流的创建
// 创建Stream 方式一:集合
@Test
public void test1(){
List<Employee> employees = EmployeeData.getEmployees();
//default Stream<E> stream():返回一个顺序流
Stream<Employee> stream = employees.stream();
// default Stream<E> parallelStream() :返回一个并行流
Stream<Employee> employeeStream = employees.parallelStream();
}
// 创建Stream 方式二:数组
@Test
public void test2(){
//public static IntStream stream(int[] array) 返回一个流
IntStream stream = Arrays.stream(new int[]{1, 2, 3, 4, 5, 6});
}
// 创建Stream 方式三:Stream
@Test
public void test3(){
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6);
}
// 创建Stream 方式四:创建无限流
@Test
public void test4(){
Stream.iterate(0,t->t+2).limit(10).forEach(System.out::println);
Stream.generate(Math::random).limit(10).forEach(System.out::println);
}
Stream的中间操作
筛选与切片
@Test
public void test1(){
final List<Employee> employees = EmployeeData.getEmployees();
employees.stream().filter(e->e.getSalary()>10000000000d).forEach(System.out::println);
//跳过5个
employees.stream().skip(5).forEach(System.out::println);
//截断流
employees.stream().limit(5).forEach(System.out::println);
//筛选:去重,根据equals和hashcode
employees.stream().distinct().forEach(System.out::println);
}
映射map
@Test
public void test2(){
final List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
list.stream().map(str->str.toUpperCase()).forEach(System.out::println);
//获取姓名长度大于5的员工的姓名
Stream<String> eStream = EmployeeData.getEmployees().stream().map(Employee::getName);
eStream.filter(s -> s.length()>5).forEach(System.out::println);
//flatMap
//map 类比add
//flatMap类比addAll:所有流连接成一个流
final Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest2::fromStringToStream);
final Stream<Character> characterStream = list.stream().flatMap(StreamAPITest2::fromStringToStream);
streamStream.forEach(s->s.forEach(System.out::println));
System.out.println("--------------------");
characterStream.forEach(System.out::println);
}
public static Stream<Character> fromStringToStream(String str){
ArrayList<Character> list1 = new ArrayList<>();
for (Character c:str.toCharArray() ) {
list1.add(c);
}
return list1.stream();
}
排序
// 排序
@Test
public void test3(){
// sorted自然排序
final List<Integer> integers = Arrays.asList(12, 43, 65, 34, 87, 0, -98, 3);
integers.stream().sorted().forEach(System.out::println);
// sorted(Comparator)定制排序
EmployeeData.getEmployees().stream().sorted((o1,o2)->Integer.compare(o1.getAge(),o2.getAge()))
.forEach(System.out::println);
}
终止操作
匹配与查找
@Test
public void test1(){
List<Employee> employees = EmployeeData.getEmployees();
//是否都满足 boolean allMatch(Predicate<? super T> predicate);
System.out.println(employees.stream().allMatch(e -> e.getAge() > 18));
// any满足
System.out.println(employees.stream().anyMatch(employee -> employee.getSalary() < 10000));
// none 满足
System.out.println(employees.stream().noneMatch(employee -> employee.getId() < 1000));
// findFirst 结果是Optional类型
System.out.println(employees.stream().findFirst());
// findAny
// count 个数
employees.stream().filter(e->e.getName().length()>5).count();
// max(Comparator c)
System.out.println(employees.stream().map(e -> e.getAge()).max(Integer::compareTo));
// min(Comparator c)
System.out.println(employees.stream().min((e1, e2) -> Double.compare(e1.getAge(), e2.getAge())));
// forEach
}
规约
@Test
public void test2(){
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
//100 + 1+2+3+4+5+6+7+8+9+10
System.out.println(list.stream().reduce(100, Integer::sum));
final List<Employee> employees = EmployeeData.getEmployees();
System.out.println(employees.stream().map(e -> e.getSalary()).reduce(0d, Double::sum));
System.out.println(employees.stream().map(e -> e.getSalary()).reduce((d1, d2) -> d1 + d2));
}
收集
@Test
public void test3(){
final List<Employee> employees = EmployeeData.getEmployees();
final List<Employee> list = employees.stream().filter(e -> e.getAge() < 35).collect(Collectors.toList());
list.stream().forEach(System.out::println);
final Set<Employee> set = employees.stream().filter(e -> e.getAge() < 35).collect(Collectors.toSet());
set.stream().forEach(System.out::println);
}
5. Optional类
- 空指针异常是导致java应用程序失败的最常见原因。为了解决空指针异常,引入了Optional类。
Optional<T>
类是一个容器类,可以保存类型T 的值,代表这个值存在。或者仅仅保存null,代表这个值不存在。原先用null来表示一个值不存在,现在IOptional可以更好的表达这个概念。并且可以避免空指针异常。- 值存在时,isPresent()返回true, get()返回该对象。
- 创建Optional类对象的方法
- Optional.of(T t):创建一个Optional实例,t必须非空
- Optional.empty():创建一个空的Optional实例
- Optional.ofNullable(T t):t可以为null
- 判断Optional容器是否包含对象
- boolean isPresent()
- void ifPresent(Consumer<? super T> action) :如果有值,则执行Consumer接口实现的代码,并且该值会作为参数传给它。
- 获取Optional容器中的对象
- T get():如果有值则将其返回,否则抛异常
- T orElse(T other):如果有值则将其返回,否则返回指定的other对象
- T orElseGet(Supplier<? extends T> supplier):如果有值则将其返回,否则返回由Supplier接口实现提供的对象
- T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X:如果有值则将其返回,否则返回由Supplier接口实现提供的异常
6. 其它(*)
- 接口的增强
- 新的日期和时间API(见日期)
- 注解:重复注解、类型注解(见注解)
- 同样目标类型推断
- JDK的更新:集合的流式操作、并发、Arrays(见集合)、Number和Math、IO/NIO的改进(见IO)、Reflection获取形参名、String:join()、Files
- 新编译工具:jjs(执行js)、jdeps
- JVM中元空间取代永久代