Java8常用新特性
文章目录
Java8新特性概述

最重要的就是
Lambda表达式和Stream API。
Lambda表达式
使用前后对比
|
|
基本语法
-
举例: (o1,o2) -> Integer.compare(o1,o2);
-
格式:
-> :lambda操作符 或 箭头操作符
->左边:lambda形参列表 (其实就是接口中的抽象方法的形参列表)
->右边:lambda体 (其实就是重写的抽象方法的方法体)
常见的六种情况


总结:
- ->左边:lambda形参列表的参数类型可以省略(类型推断);如果lambda形参列表只有一个参数,其一对()也可以省略。
- ->右边:lambda体应该使用一对{}包裹;如果lambda体只有一条执行语句(可能是return语句),省略这一对{}和return关键字
代码展示:
|
|
函数式接口
使用说明
-
如果一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口。我们可以在一个接口上使用
@FunctionalInterface注解,这样做可以检查它是否是一个函数式接口。 -
Lambda表达式实际上就是函数式接口的实例。
具体使用


总结
-
何时使用Lambda表达式?
答:当需要对一个函数式接口实例化的时候,可以使用Lambda表达式。
-
何时使用给定的函数式接口?
答:如果我们开发中需要定义一个函数式接口,首先查看已有的JDK提供的函数式接口中,是否提供了满足需求的函数式接口,如果有,直接调用即可,无需再自定义。
方法引用
基本语法
|
|
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
方法引用,本质上就是Lambda表达式,而Lambda表达式作为函数式接口的实例。所以方法引用,也是函数式接口的实例。
三种情况
- **情况一:**对象 :: 非静态方法
- **情况二:**类 :: 静态方法
- **情况三:**类 :: 非静态方法
要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的形参列表和返回值类型相同!(针对于情况1和情况2)
注意:当函数式接口方法的第一个参数是需要引用方法的调用者,并且第二 个参数是需要引用方法的参数(或无参数)时:ClassName::methodName(针对情况三)
代码展示:
|
|
构造器引用与数组引用
基本语法
-
构造器引用格式
1类名::new -
数组引用格式
1数组类型[]::new
使用要求
-
构造器引用
和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。抽象方法的返回值类型即为构造器所属的类的类型。
-
数组引用
大家可以把数组看做是一个特殊的类,则写法与构造器引用一致。
代码实例
-
构造器引用
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 45 46 47 48 49 50 51//构造器引用 //Supplier中的T get() //Employee的空参构造器:Employee() @Test public void test1(){ Supplier<Employee> sup = new Supplier<Employee>() { @Override public Employee get() { return new Employee(); } }; System.out.println("*******************"); Supplier<Employee> sup1 = () -> new Employee(); System.out.println(sup1.get()); System.out.println("*******************"); Supplier<Employee> sup2 = Employee :: new; System.out.println(sup2.get()); } //Function中的R apply(T t) @Test public void test2(){ Function<Integer,Employee> func1 = id -> new Employee(id); Employee employee = func1.apply(1001); System.out.println(employee); System.out.println("*******************"); Function<Integer,Employee> func2 = Employee :: new; Employee employee1 = func2.apply(1002); System.out.println(employee1); } //BiFunction中的R apply(T t,U u) @Test public void test3(){ BiFunction<Integer,String,Employee> func1 = (id,name) -> new Employee(id,name); System.out.println(func1.apply(1001,"Tom")); System.out.println("*******************"); BiFunction<Integer,String,Employee> func2 = Employee :: new; System.out.println(func2.apply(1002,"Tom")); } -
数组引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14//数组引用 //Function中的R apply(T t) @Test public void test4(){ Function<Integer,String[]> func1 = length -> new String[length]; String[] arr1 = func1.apply(5); System.out.println(Arrays.toString(arr1)); System.out.println("*******************"); Function<Integer,String[]> func2 = String[] :: new; String[] arr2 = func2.apply(10); System.out.println(Arrays.toString(arr2));
Stream API
Stream关注的是对数据的运算,与CPU打交道;而集合关注的是数据的存储,与内存打交道。
java8提供了一套api,可以对内存中的数据进行过滤、排序、映射、规约等操作。类似于sql对数据库中表相关操作。
注意点
-
Stream 自己不会存储元素。
-
Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
-
Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
使用流程
- Stream的实例化
- 一系列的中间操作(过滤、映射…)
- 终止操作
注意:
- 一个中间操作链,对数据源的数据进行处理
- 一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用
步骤一:Stream的实例化
|
|
步骤二:Stream的中间操作
-
筛选和切片
方 法 描 述 filter(Predicate p) 接收 Lambda , 从流中排除某些元素 distinct() 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素 limit(long maxSize) 截断流,使其元素不超过给定数量 skip(long n) 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一 个空流。与 limit(n) 互补 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//1-筛选与切片 @Test public void test1(){ List<Employee> list = EmployeeData.getEmployees(); // filter(Predicate p)——接收 Lambda , 从流中排除某些元素。 Stream<Employee> stream = list.stream(); //练习:查询员工表中薪资大于7000的员工信息 stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println); System.out.println(); // limit(n)——截断流,使其元素不超过给定数量。 list.stream().limit(3).forEach(System.out::println); System.out.println(); // skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补 list.stream().skip(3).forEach(System.out::println); System.out.println(); // distinct()——筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素 list.add(new Employee(1010,"刘强东",40,8000)); list.add(new Employee(1010,"刘强东",41,8000)); list.add(new Employee(1010,"刘强东",40,8000)); list.add(new Employee(1010,"刘强东",40,8000)); list.add(new Employee(1010,"刘强东",40,8000)); // System.out.println(list); list.stream().distinct().forEach(System.out::println); } -
映射
方法 描述 map(Function f) 接收一个函数作为参数,该函数会被应用到每个元 素上,并将其映射成一个新的元素。 mapToDouble(ToDoubleFunction f) 接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 DoubleStream。 mapToInt(ToIntFunction f) 接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 IntStream。 mapToLong(ToLongFunction f) 接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 LongStream。 flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另 一个流,然后把所有流连接成一个流 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 45 46 47 48 49 50 51 52 53//映射 @Test public void test2(){ // map(Function f)——接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素。 List<String> list = Arrays.asList("aa", "bb", "cc", "dd"); list.stream().map(str -> str.toUpperCase()).forEach(System.out::println); // 练习1:获取员工姓名长度大于3的员工的姓名。 List<Employee> employees = EmployeeData.getEmployees(); Stream<String> namesStream = employees.stream().map(Employee::getName); namesStream.filter(name -> name.length() > 3).forEach(System.out::println); System.out.println(); //练习2: Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest1::fromStringToStream); streamStream.forEach(s ->{ s.forEach(System.out::println); }); System.out.println(); // flatMap(Function f)——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。 Stream<Character> characterStream = list.stream().flatMap(StreamAPITest1::fromStringToStream); characterStream.forEach(System.out::println); } //将字符串中的多个字符构成的集合转换为对应的Stream的实例 public static Stream<Character> fromStringToStream(String str){//aa ArrayList<Character> list = new ArrayList<>(); for(Character c : str.toCharArray()){ list.add(c); } return list.stream(); } @Test public void test3(){ ArrayList list1 = new ArrayList(); list1.add(1); list1.add(2); list1.add(3); ArrayList list2 = new ArrayList(); list2.add(4); list2.add(5); list2.add(6); // list1.add(list2); list1.addAll(list2); System.out.println(list1); } -
排序
方法 描述 sorted() 产生一个新流,其中按自然顺序排序 sorted(Comparator com) 产生一个新流,其中按比较器顺序排序 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//3-排序 @Test public void test4(){ // sorted()——自然排序 List<Integer> list = Arrays.asList(12, 43, 65, 34, 87, 0, -98, 7); list.stream().sorted().forEach(System.out::println); //抛异常,原因:Employee没有实现Comparable接口 // List<Employee> employees = EmployeeData.getEmployees(); // employees.stream().sorted().forEach(System.out::println); // sorted(Comparator com)——定制排序 List<Employee> employees = EmployeeData.getEmployees(); employees.stream().sorted( (e1,e2) -> { int ageValue = Integer.compare(e1.getAge(),e2.getAge()); if(ageValue != 0){ return ageValue; }else{ return -Double.compare(e1.getSalary(),e2.getSalary()); } }).forEach(System.out::println); }
步骤三:Stream的终止操作
-
匹配与查找
方法 描述 allMatch(Predicate p) 检查是否匹配所有元素 anyMatch(Predicate p) 检查是否至少匹配一个元素 noneMatch(Predicate p) 检查是否没有匹配所有元素 findFirst() 返回第一个元素 findAny() 返回当前流中的任意元素 count() 返回流中元素总数 max(Comparator c) 返回流中最大值 min(Comparator c) 返回流中最小值 forEach(Consumer c) 内部迭代(使用 Collection 接口需要用户去做迭代, 称为外部迭代。相反,Stream API 使用内部迭 代——它帮你把迭代做了) 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 45 46 47 48 49 50//1-匹配与查找 @Test public void test1(){ List<Employee> employees = EmployeeData.getEmployees(); // allMatch(Predicate p)——检查是否匹配所有元素。 // 练习:是否所有的员工的年龄都大于18 boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18); System.out.println(allMatch); // anyMatch(Predicate p)——检查是否至少匹配一个元素。 // 练习:是否存在员工的工资大于 10000 boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000); System.out.println(anyMatch); // noneMatch(Predicate p)——检查是否没有匹配的元素。 // 练习:是否存在员工姓“雷” boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("雷")); System.out.println(noneMatch); // findFirst——返回第一个元素 Optional<Employee> employee = employees.stream().findFirst(); System.out.println(employee); // findAny——返回当前流中的任意元素 Optional<Employee> employee1 = employees.parallelStream().findAny(); System.out.println(employee1); } @Test public void test2(){ List<Employee> employees = EmployeeData.getEmployees(); // count——返回流中元素的总个数 long count = employees.stream().filter(e -> e.getSalary() > 5000).count(); System.out.println(count); // max(Comparator c)——返回流中最大值 // 练习:返回最高的工资: Stream<Double> salaryStream = employees.stream().map(e -> e.getSalary()); Optional<Double> maxSalary = salaryStream.max(Double::compare); System.out.println(maxSalary); // min(Comparator c)——返回流中最小值 // 练习:返回最低工资的员工 Optional<Employee> employee = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())); System.out.println(employee); System.out.println(); // forEach(Consumer c)——内部迭代 employees.stream().forEach(System.out::println); //使用集合的遍历操作 employees.forEach(System.out::println); } -
归约
方法 描述 reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一 个值。返回 T reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一 个值。返回 Optional 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19//2-归约 @Test public void test3(){ // reduce(T identity, BinaryOperator)——可以将流中元素反复结合起来,得到一个值。返回 T // 练习1:计算1-10的自然数的和 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); // reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值。返回 Optional<T> // 练习2:计算公司所有员工工资的总和 List<Employee> employees = EmployeeData.getEmployees(); Stream<Double> salaryStream = employees.stream().map(Employee::getSalary); // Optional<Double> sumMoney = salaryStream.reduce(Double::sum); Optional<Double> sumMoney = salaryStream.reduce((d1,d2) -> d1 + d2); System.out.println(sumMoney.get()); } -
收集
方法 描述 collect(Collector c) 将流转换为其他形式。接收一个 Collector 接口的实现,用于给Stream中元素做汇总 的方法 Collector需要使用Collectors提供实例:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16//3-收集 @Test public void test4(){ // collect(Collector c)——将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法 // 练习1:查找工资大于6000的员工,结果返回为一个List或Set List<Employee> employees = EmployeeData.getEmployees(); List<Employee> employeeList = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList()); employeeList.forEach(System.out::println); System.out.println(); Set<Employee> employeeSet = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet()); employeeSet.forEach(System.out::println); }
Optional类的使用
-
为了解决Java中的空指针的问题,而使用了Optional类。
-
Optional
类(java.util.Optional) 是一个容器类,它可以保存类型T的值,代表 这个值存在。或者仅仅保存null,表示这个值不存在。原来用 null 表示一个值不 存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。
|
|
文章作者 Bearkchan
上次更新 2020-03-02