Java8 新特性
1、Lambda 表达式的使用
举例:(o1,o2) -> Integer.compare(o1,o2);
格式:
- -> :lambda操作符 或箭头操作符
- 左边:lambda 形参列表(其实就是接口中的抽象方法的形参列表)
- 右边:lambda 体(其实就是重写的抽象方法的方法体)
lambda 表达式的使用:(分为6种情况介绍)
无参,无返回值
Runnable r1 = ()-> System.out.println("哈哈哈哈哈哈哈");
Lambda 需要一个参数,但是没有返回值
Consumer<String> con = (String s)-> System.out.println(s); con.accept("谎言和誓言的区别是什么呢?");
数据类型可以省略,因为可由编译器推断得出,称为 “类型推断”
Consumer<String> con = (s)-> System.out.println(s); con.accept("一个是听的人当真了,一个是说的人当真了");
Lambda 若只需要一个参数时,参数的小括号可以省略
Consumer<String> con = s -> System.out.println(s);
Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值
Comparator<Integer> com = (o1,o2) -> { System.out.println(o1); System.out.println(o2); return o1.compareTo(o2); }; System.out.println(com.compare(12,21));
当 Lambda 体只有一条语句时,return 与大括号若有,都可以省略
Comparator<Integer> com1 = (o1,o2) -> o1.compareTo(o2);
lambda 表达式的本质:作为接口的实例
2、函数式接口
1、自定义函数式接口
1、如果一个接口中,只声明了一个抽象方法,则此就扣就称为函数式接口
2、自定义函数式接口
接口中只有一个方法就是函数式接口;
标注注解
@FunctionalInterface
是起到一个校验的作用,如果存在两个方法,注解就会报错;不标注注解也是一个函数式接口,注解只是校验作用
@FunctionalInterface public interface MyInterface { void method(); }
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 断定型接口 (返回 boolean 类型数据) | T | boolean | 确定类型为 T 的对象是否满足某约束,并返回 boolean 值。包含方法:boolean test(T t); |
3、测试
1、消费型接口
@Test
void test04(){
// lambda 表达式
happlyTime(500.00,money -> System.out.println("买瓶矿泉水,价格是:"+money));
}
/**
*
* @param money 金额
* @param com 消费型接口,可直接使用 Lambda 表达式
*/
public void happlyTime(Double money,Consumer<Double> com){
com.accept(money);
}
2、断定型接口
@Test
void test05(){
List<String> list = Arrays.asList("北京", "天津", "南京", "东京", "西经", "普京");
List<String> filterStrs = filterString(list, s -> s.contains("京"));
System.out.println(filterStrs);
}
// 根据给定的规则,过滤集合中的字符串,规则由Predicate的实现给定
public List<String> filterString(List<String> list, Predicate<String> pre){
ArrayList<String> arrayList = new ArrayList<>();
for (String s : list) {
if(pre.test(s)){
arrayList.add(s);
}
}
return arrayList;
}
3、方法引用与构造器引用
1、方法引用
使用情景:当要传递给 Lambda 体的操作,已经有实现的方法了,可以使用方法引用
方法引用,本质上就是 Lambda 表达式,而 Lambda 表达式作为函数式接口的实例。所以方法引用,也是函数式接口的实例
使用格式:类(或对象):: 方法名
具体分为如下的三种情况
对象 :: 非静态方法
使用要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的形参列表和返回值类型相同
Employee emp = new Employee(1001,"Tom",23,5600); Supplier<String> sup2 = emp::getName; System.out.println(sup2.get());
类 :: 静态方法
使用要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的形参列表和返回值类型相同
Comparator<Integer> com = Integer::compare; System.out.println(com.compare(12,32));;
类 :: 非静态方法
Function<Employee,String> fun = Employee::getName; System.out.println(fun.apply(new Employee("Tom")));;
2、构造器引用
和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。
抽象方法的返回值类型即为构造器所属的类的类型
对象中必须存在,Integer 的构造器,否则不能使用
测试一:
public Employee(Integer id){ this.id = id; }
Function<Integer,Employee> fun = Employee::new; Employee apply = fun.apply(1001); System.out.println(apply);
测试二:
// 因为 Employee 中存在这个有参构造 public Employee(Integer id,String name){ this.id = id; this.name = name; }
BiFunction<Integer,String,Employee> bi = Employee::new; Employee employee = bi.apply(1001, "Tom"); System.out.println(employee);
3、数组引用
可以把数组看做是一个特殊的类,则写法与构造器引用一致。
测试:
Function<Integer,String[]> fun = String[]::new; String[] apply = fun.apply(10); System.out.println(Arrays.asList(apply));
4、Stream API
Stream 关注的是对数据的运算,与CPU打交道
集合关注的数据的存储,与内存打交道
Stream 自己不会存储元素
Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream
Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行
执行流程:
- 实例化
- 一系列中间操作(过滤、映射)
- 终止操作
1、创建方式
通过集合
list.stream();// 顺序流 list.parallelStream();// 并行流,几个线程同时去获取数据
通过数组
// 通过 Arrays Arrays.stream(各种类型);
通过 Stream 的 of()
// 可以调用Stream类静态方法 of(),通过显示值创建一个流。它可以接收任意数量的参数 Stream.of(1,2,3,4,5,6,7);
创建无限流
2、筛选与切片
方法 描述 filter(Predicate p) 接收 Lambda ,从流中排除某些元素 distinct() 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素 limit(long maxSize) 截断流,使其元素不超过给定数量 skip(long n) 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
使用:
filter
List<Employee> data = EmployeeData.getData(); // 过滤掉员工id小于5的数据 data.stream().filter(e->e.getId()>5).forEach(System.out::println);
limit
// 只获取前三个数据 data.stream().limit(3).forEach(System.out::println);
skip
// 跳过前三个数据 data.stream().skip(3).forEach(System.out::println);
distinct
List<Integer> list = new ArrayList<>(); list.add(1); list.add(1); list.add(1); list.add(1); list.add(1); list.add(2); list.add(2); list.add(2); list.add(2); list.add(2); list.add(2); list.add(3); // 去重 list.stream().distinct().forEach(System.out::println);
3、映射
Map
练习:获取员工id大于5的所有员工的姓名
List<Employee> data = EmployeeData.getData(); data.stream().filter(e->e.getId()>5) .map(Employee::getName) .forEach(System.out::println);
flatMap
接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
@Test void test12(){ ArrayList<String> list = new ArrayList<>(); list.add("aa"); list.add("bb"); list.add("cc"); // 这样操作返回的是一个嵌套的stream流 Stream<Stream<Character>> streamStream = list.stream() .map(LambdaTestApplicationTests::formStringToStream); streamStream.forEach(s->{ s.forEach(System.out::println); }); // flatMap list.stream().flatMap(LambdaTestApplicationTests::formStringToStream) .forEach(System.out::println); } // 数据准备 public static Stream<Character> formStringToStream(String str){ ArrayList<Character> list = new ArrayList<>(); for (Character c : str.toCharArray()){ list.add(c); } return list.stream(); }
4、排序
方法 描述 sorted() 产生一个新流,其中按自然顺序排序 sorted(Comparator com) 产生一个新流,其中按比较器顺序排序 // 自然排序 List<Integer> integers = Arrays.asList(1, 43, 556, 234, 423654, 4363); integers.stream().sorted().forEach(System.out::println); // 抛异常,原因 Employee 没有实现 Comparable 接口 // List<Employee> data = EmployeeData.getData(); // data.stream().sorted().forEach(System.out::println); // 定制排序 List<Employee> data = EmployeeData.getData(); data.stream().sorted((e1,e2)->Integer.compare(e2.getId(), e1.getId())).forEach(System.out::println);
5、匹配与查找
方法 描述 allMatch(Predicate p) 检查是否匹配所有元素 anyMatch(Predicate p) 检查是否至少匹配一个元素 noneMatch(Predicate p) 检查是否没有匹配所有元素 findFirst() 返回第一个元素 findAny() 返回当前流中的任意元素
// allMatch:所有员工是否都大于18岁 List<Employee> data = EmployeeData.getData(); boolean allMatch = data.stream().allMatch(e -> e.getAge() > 18); // anyMatch:是否存在年龄大于39的员工 boolean anyMatch = data.stream().anyMatch(e -> e.getAge() > 39); // noneMatch(检查是否没有匹配的元素):是否存在年龄大于39的员工 boolean noneMatch = data.stream().noneMatch(e -> e.getAge() > 39); // findFirst:取第一条元素 Optional<Employee> first = data.stream().findFirst(); // findAny:取任意元素(随机,但是stream默认是排序的,只会取到第一条数据) Optional<Employee> any = data.stream().findAny(); System.out.println(any); // 并行化会取一个任意元素 Optional<Employee> any1 = data.parallelStream().findAny(); System.out.println(any1);
方法 描述 count 返回流中元素总个数 max(Comparator c) 返回流中最大值 min(Comparator c) 最小值 forEach(Consumer c) 内部迭代
// count:员工总数 List<Employee> data = EmployeeData.getData(); long count = data.stream().count(); System.out.println(count); // max:年龄最大的员工 Optional<Employee> max = data.stream().max(Comparator.comparingInt(Employee::getAge)); System.out.println(max); // min:年龄最小的员工 Optional<Employee> min = data.stream().min((e1, e2) -> Integer.compare(e1.getAge(), e2.getAge())); System.out.println(min); // forEach:内部迭代 data.stream().forEach(System.out::println); // 使用集合的遍历操作 data.forEach(System.out::println);
6、归约
方法 描述 reduce(T iden,BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 T reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 Optional 备注:map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它来进行网络搜索而出名
// 计算1-10的总和 List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); Integer reduce = integers.stream().reduce(0, Integer::sum); System.out.println(reduce); // 计算所有员工年龄的总和 List<Employee> data = EmployeeData.getData(); Optional<Integer> reduce1 = data.stream().map(Employee::getAge).reduce(Integer::sum); System.out.println(reduce1);
7、收集
方法 描述 collect(Collector c) 将流转换为其他形式。接收一个 Collector 接口的实现,用于给 Stream 中元素做汇总的方法 Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到List、Set、Map)
另外,Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例见官方文档:
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html
List<Employee> collect = data.stream().filter(e -> e.getAge() < 30).collect(Collectors.toList()); Set<Employee> set = data.stream().filter(e -> e.getAge() < 30).collect(Collectors.toSet()); // 封装map,id是key,name是value Map<Integer, String> map = data.stream().filter(e -> e.getAge() < 30) .collect(Collectors.toMap(Employee::getId, Employee::getName));
5、Optional 类
- Optional 类(java.utill.Optional) 是一个容器类,它可以保存类型 T 的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。
- Optional 类的 Javadoc 描述如下:这是一个可以为null的容器对象。如果值存在则 isPresent() 方法会返回 true ,调用 get() 方法会返回该对象
Optional 提供很多有用的方法,这样我们就不用显式进行控制检测。
创建 Optional 类对象的方法:
Optional.of(T t):创建一个 Optional 实例,t 必须非空;
Employee employee = new Employee(); employee=null; // of(T t):保证 t 是非空的,空的会报空指针 Optional<Employee> optionalEmployee = Optional.of(employee);
Optional.empty():创建一个空的 Optional 实例
Optional.ofNullable(T t):t可以为null
Employee employee = new Employee(); employee=null; // ofNullable(T t):t 可以为空 Optional<Employee> optionalEmployee = Optional.ofNullable(employee);
判断 Optional 容器中是否包含对象:
- boolean isPresent():判断是否包含对象
- void ifPresent(Consumer<? super T> consumer):如果有值,就执行 Consumer 接口的实现代码,并且该值会作为参数传给它
获取 Optional 容器的对象
- T get():如果调用对象包含值,返回该值,否则抛异常
- T orElse(T other):如果有值则将其返回,否则返回指定的 other 对象。
- T orElseGet(Supplier<? extends T> other):如果有值则将其返回,否则返回由 Supplier 接口实现提供的对象。
- T orElseThrow(Supplier<> extends X> exceptionSupplier):如果有值则将其返回,否则抛出由 Supplier 接口实现提供的异常
public String getGirlName(Boy boy){ Optional<Boy> optionalBoy = Optional.ofNullable(boy); // 此时的boy1一定非空 Boy boy1 = optionalBoy.orElse(new Boy(new Girl("迪丽热巴"))); Girl girl = boy1.getGirl(); Optional<Girl> optionalGirl = Optional.ofNullable(girl); // 此时的girl1一定非空 Girl girl = optionalGirl.orElse(new Girl("古力娜扎")); return girl.getName(); } @Test void test(){ Boy boy = null; String girlName = getGirlName(boy); System.out.println(girlName); // 结果:迪丽热巴 }