14.1 函数式接口

只包含一个抽象方法的接口称为函数式接口。 使用@FunctionalInterface 可以检查是否是函数式接口。Java内置了四大核心函数式接口以及一些其他接口。

函数式接口 用途
Consumer消费型接口 void accept(T t)
Supplier供给型接口 T get()
Function函数型接口 R apply(T t)
Predicate断言型接口 boolean test(T t)
  1. // 函数式接口举例
  2. @FunctionalInterface
  3. public interface MyFunc<T> {
  4. public T getValue(T t);
  5. }

14.2 Lambda表达式

Lambda用于实例化函数式接口对象。以前用匿名实现类表示的现在都可以用Lambda表达式来写。

格式:(参数列表) -> {逻辑实现}

  • 左侧:通过声明变量时即可推断出类型,因此可省略参数类型;若参数列表仅一个参数则可省略括号。
  • 右侧:如果仅有一条语句可以省略 {}和return 。 ```java // 语法格式一:无参,无返回值 Runnable r1 = () -> { System.out.println(“Hello”); };

//语法格式二:有参数,但是没有返回值。 Consumer consumer = (str) -> { System.out.println(str); }

//语法格式三:Lambda 若只需要一个参数时,参数的小括号可以省略 Consumer consumer = str -> { System.out.println(str); }

//语法格式四:Lambda 可以有两个或以上的参数,多条执行语句,返回值 Comparator com = (o1,o2) -> { System.out.println(o1); System.out.println(o2); return o1.compareTo(o2); }; System.out.println(com.compare(1,2)); // 使用接口引用调用接口实现方法

//语法格式五:当 Lambda 体只有一条语句时,return 、{ } 都可以省略 Comparator com = (o1,o2) -> o1.compareTo(o2);

  1. <a name="FhyrK"></a>
  2. ## 14.3 引用
  3. <a name="tgM42"></a>
  4. ### 14.3.1 方法引用
  5. 当要传递给Lambda体的操作已经有实现的方法了,可以使用方法引用!这就要求实现接口抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致!
  6. 格式:使用操作符 “**::**” 将类(或对象) 与方法名分隔开来。
  7. ```java
  8. // 等价例子1
  9. Consumer<String> con = (x) -> System.out.println(x);
  10. Consumer<String> con1 = System.out :: println;
  11. // 等价例子2
  12. Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
  13. Comparator<Integer> com1 = Integer::compare;
  14. // 等价例子3
  15. BiPredicate<String, String> bp = (x, y) -> x.equals(y);
  16. BiPredicate<String, String> bp1 = String::equals;

14.3.2 构造器引用

可以把构造器引用赋值给定义的方法,要求构造器参数列表要与接口中抽象方法的参数列表一致!且方法的返回值即为构造器对应类的对象。

格式: ClassName::new

  1. Function<Integer, MyClass> fun = (n) -> new MyClass(n);
  2. Function<Integer, MyClass> fun1 = MyClass::new;

格式:type[] :: new

  1. Function<Integer, Integer[]> fun = (n) -> new Integer[n];
  2. Function<Integer, Integer[]> fun1 = Integer[]::new;

14.4 Stream API

Stream API 提供了一种高效且易于使用的处理集合数据的方式。

操作步骤:

  1. 创建Stream:一个数据源(如:集合、数组)获取一个流
  2. 中间操作:对数据源的数据进行处理,不执行终止操作前不执行
  3. 终止操作:一旦执行终止操作,就执行中间操作链,并产生结果。之后不能再使用

image.png

  1. // 通过集合创建流:Collection 提供了两个获取流的方法
  2. default Stream<E> stream() // 返回一个顺序流
  3. default Stream<E> parallelStream() // 返回一个并行流
  4. // 通过数组创建流: Arrays 的静态方法 stream() 可以获取数组流
  5. static <T> Stream<T> stream(T[] array)
  6. // 通过Stream的静态方法of()创建流
  7. public static<T> Stream<T> of(T... values)
  8. // 创建无限流(无限个数据对应的Stream,主要用于造数据)
  9. public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) // 迭代
  10. public static<T> Stream<T> generate(Supplier<T> s) // 生成
  11. // 创建无限流的例子
  12. // 从0开始,每次加2的流;中间操作为取前十个数据,终止操作为输出
  13. Stream<Integer> stream = Stream.iterate(0, x -> x + 2);
  14. stream.limit(10).forEach(System.out::println);
  15. // 生成随机数的流;中间操作为取前十个数据,终止操作为输出
  16. Stream<Double> stream1 = Stream.generate(Math::random);
  17. stream1.limit(10).forEach(System.out::println);
  1. // 1-筛选与切片
  2. filter(Predicate p) // 接收 Lambda, 从流中排除某些元素
  3. distinct() // 通过流元素的 hashCode() 和 equals() 去重
  4. limit(long maxSize) // 截断流,使其元素不超过给定数量
  5. skip(long n) // 返回扔掉前 n 个元素的流。若流中元素不足 n 个,则返回个空流
  6. // 2-映射
  7. map(Function f) // 将函数应用到流中每个元素上
  8. flatMap(Function f) // 将流中的每个值都换成另一个流,然后把所有流连接成一个流。该方法用于处理集合套集合的情况。例如[1,2,3,[2,3,4]]此种类型的集合,使用map处理完之后集合中有4个元素,而使用floatMap处理完之后,集合中有6个元素
  9. // 3-排序
  10. sorted() // 产生一个新流,按自然顺序排序
  11. sorted(Comparator com) // 产生一个新流,按比较器顺序排序

终止操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void 。 流执行了终止操作后,将不能再次使用。

  1. // 1-匹配与查找
  2. allMatch(Predicate p) // 检查是否匹配所有元素
  3. anyMatch(Predicate p) // 检查是否至少匹配一个元素
  4. noneMatch(Predicate p) // 检查是否没有匹配所有元素
  5. findFirst() // 返回第一个元素
  6. findAny() // 返回当前流中的任意元素
  7. count() // 返回流中元素总数
  8. max(Comparator c) // 返回流中最大值
  9. min(Comparator c) // 返回流中最小值
  10. forEach(Consumer c) // 数据迭代
  11. // 2-归约
  12. reduce(T iden, BinaryOperator b) // 在初始情况为iden的情况下,使用BinaryOperator反复操作合计并累计起来,返回一个值
  13. // 例子:计算1-10的自然数的和
  14. List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
  15. Integer sum1 = list.stream().reduce(0, Integer::sum);
  16. Integer sum2 = list.stream().reduce(10, (x, y) -> x + y);
  17. System.out.println(sum1 + " " + sum2);
  18. // 55 65
  19. // 3-收集
  20. collect(Collector c) // 将Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、Map)。

Collectors 类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法如下:

  1. // toList List<T> 把流中元素收集到List
  2. List<Employee> emps= list.stream().collect(Collectors.toList());
  3. // toSet Set<T> 把流中元素收集到Set
  4. Set<Employee> emps= list.stream().collect(Collectors.toSet());
  5. // counting Long 计算流中元素的个数
  6. long count = list.stream().collect(Collectors.counting());
  7. // summingInt Integer 对流中元素的整数属性求和
  8. int total=list.stream().collect(Collectors.summingInt(Employee::getSalary));
  9. // averagingInt Double 计算流中元素Integer属性的平均值
  10. double avg = list.stream().collect(Collectors.averagingInt(Employee::getSalary));
  11. // joining String 连接流中每个字符串
  12. String str= list.stream().map(Employee::getName).collect(Collectors.joining());
  13. // maxBy Optional<T> 根据比较器选择最大值
  14. Optional<Emp>max= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)));
  15. // groupingBy Map<K, List<T>> 根据某属性值对流分组,属性为K,结果为V
  16. Map<Emp.Status, List<Emp>> map= list.stream().collect(Collectors.groupingBy(Employee::getStatus));
  17. // partitioningBy Map<Boolean, List<T>> 根据true或false进行分区
  18. Map<Boolean,List<Emp>> vd = list.stream().collect(Collectors.partitioningBy(Employee::getManage));

14.5 Optional类

Optional 类是一个容器类,它仅保存一个类型T的值,代表这个值存在,或者保存null,表示这个值不存在。可以避免空指针异常。

  1. // 创建Optional类
  2. Optional.of(T t) // 创建一个 Optional 实例,t必须非空;
  3. Optional.empty() // 创建一个空的 Optional 实例
  4. Optional.ofNullable(T t) // t可以为null
  5. // 判断Optional容器是否包含对象:
  6. boolean isPresent() : // 判断是否包含对象
  7. void ifPresent(Consumer<? super T> consumer) // 如果有值,就执行Consumer接口的实现代码
  8. // 获取Optional容器包含的对象:
  9. T get() // 包含则返回该值,否则抛异常
  10. // 通过下述方法可以保证不会返回null
  11. T orElse(T other) // 如果有值则将其返回,否则返回指定的other对象。
  12. T orElseGet(Supplier<? extends T> other) // 如果有值则将其返回,否则返回由Supplier接口实现提供的对象。
  13. T orElseThrow(Supplier<? extends X> exceptionSupplier) // 如果有值则将其返回,否则抛出由Supplier接口实现提供的异常。

下面展示一些使用Optional类避免空指针异常的情况。

  1. public void test1() {
  2. Boy b = new Boy("张三");
  3. Optional<Girl> opt = Optional.ofNullable(b.getGrilFriend()); // getGrilFriend可能返回null
  4. // 如果女朋友存在就打印女朋友的信息
  5. opt.ifPresent(System.out::println);
  6. // 如果有女朋友就返回他的女朋友,否则只能欣赏“嫦娥”了
  7. Girl girl = opt.orElse(new Girl("嫦娥"));
  8. System.out.println("他的女朋友是:" + girl.getName());
  9. }
  10. public void test2{
  11. Optional<Employee> opt = Optional.of(new Employee("张三", 8888));
  12. //判断opt中员工对象是否满足条件,如果满足就保留,否则返回空
  13. Optional<Employee> emp = opt.filter(e -> e.getSalary()>10000);
  14. System.out.println(emp);
  15. }