image.png
image.png
image.png
image.png

1. Lambda表达式

1.1 为什么使用Lambda表达式

Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递) 。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
举例

  1. 一般书写格式:
  2. Comparator<Integer> com1 = new Comparator<Integer>() {
  3. @Override
  4. public int compare(Integer o1, Integer o2) {
  5. return Integer.compare(o1,o2);
  6. }
  7. };
  8. lambda表达式:
  9. Comparator<Integer> com2 = (o1,o2) -> Integer.compare(o1,o2);

1.2格式

  • 基本格式:

-> :lambda操作符 or 箭头操作符
->左边:lambda形参列表(其实就是接口中抽象方法的参数列表)
->右边:lambda体(就是重写的方法的方法体,注意不用return)

  • 语法格式一:无参数 无返回值

image.png
接口中抽象方法没有参数 故lambda表达式中也没有;重写方法没有返回值,故lambda表达式也没有返回值

  • 语法格式二:需要一个参数 无返回值

接口中抽象方法需要一个参数,但是返回值为空;Lambda表达式中 (String t) -> 函数体

  1. Consumer<String> con = new Consumer<String>() {
  2. @Override
  3. public void accept(String s) {
  4. System.out.println("s = " + s);
  5. }
  6. };
  7. con.accept("学习");
  8. Consumer<String> LambdaCon = (String t) -> System.out.println("t = " + t);
  9. LambdaCon.accept("快乐") ;
  • 语法格式三: 数据类型可以省略,因为可由编译器推断出来,称为类型推断

image.png

  1. //类型推断
  2. List<String> list = new ArrayList<>();
  3. int[] arr = {1,2,3};
  • 语法格式四: Lambda表达式只需要一个参数的时候,参数的小括号可以省略

    1. Consumer<String> consumer = new Consumer<String>() {
    2. @Override
    3. public void accept(String s) {
    4. System.out.println(s);
    5. }
    6. };
    7. consumer.accept("谎言和誓言");
    8. Sysout1.out.println("***********************");
    9. Consumer<String> LambdaCon = s -> System.out.println(s);
    10. LambdaCon.accept("一个是听的人当真了,一个是说的人当真了");
  • 语法格式五:Lambda表达式需要两个及以上参数,多条执行语句,并且可以拥有返回值。

当lambda表达式参数不止一个的时候,必须使用小括号(),因为类型推断机制,可以不声明参数的类型,多个参数使用逗号分隔。当lambda体有多条执行语句的时候必须加上{}。当方法体只有一条rerun语句的时候,lambda表达式中可以省略return;当有多条语句的时候,返回值需要添加return。

  1. Comparator<Integer> com = new Comparator<Integer>() {
  2. @Override
  3. public int compare(Integer o1, Integer o2) {
  4. System.out.println("o1 = " + o1);
  5. System.out.println("o2 = " + o2);
  6. return Integer.compare(o1,o2);
  7. }
  8. };
  9. com.compare(12,20);
  10. System.out.println("******************************");
  11. Comparator<Integer> lambdaCon = (o1, o2) -> {
  12. System.out.println("o1 = " + o1);
  13. System.out.println("o2 = " + o2);
  14. return Integer.compare(o1,o2);
  15. };
  16. lambdaCon.compare(25, 15);
  17. Comparator<Integer> lambdaCon2 = (o1, o2) -> Integer.compare(o1,o2);
  18. System.out.println(lambdaCon2.compare(30,03));

总结:

1.3Lambda表达式的本质

java—万事万物皆对象
lambda表达式在java中依然作为对象 —-接口的实例对象
Comparator<Integer> lambdaCon2 = (o1, o2) -> Integer.compare(o1,o2);声明对象
**

2. 函数式接口

如果一个接口中,只声明了一个抽象方法,则此接口称为函数式接口
●你可以通过Lambda表达式来创建该接口的对象。( 若Lambda表达式抛出一一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明),
●我们可以在一个接口, 上使用@FunctionalInterface注解,这样做可以检查它是否是一个函数式接口。同javadoc也会包含一条声明,说明这个接口是一个函数式接口。
●在java.util.function包下定 义了Java 8的丰富的函数式接口

2.1如何理解函数式接口

● Java从诞生日起就是一直倡导“一切皆对象”,在Java里面面向对象(OOP)编程就是一切。但是随着python、scala等语言的兴起和新技术的挑战,Java不得不做出调整以便支持更加广泛的技术要求,也即java不但可以支持OOP还可以支持OOF(面向函数编程)。
● 在函数式编程语言当中,函数被当做一等公民对待。在将函数作为一等公民的编程语言中,Lambda表达式的类型是函数。但是在Java8中,有所不同。在Java8中,Lambda表达式是对象,而不是函数,它们必须依附于一类特别的
对象类型——函数式接口。
● 简单的说,在Java8中,Lambda表达式就是一个函数式接口的实例。这就是Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口的实例,那么该对象就可以用Lambda表达式来表示。
● 所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。

2.2java内置的函数式接口

image.png

  1. /**
  2. * 消费型接口 Consumer<T> void accept(T t)
  3. * 供给型接口 Supplier<T> T get()
  4. * 函数型接口 Function<T,R> R apply(T t)
  5. * 断定型接口 boolean test(T t)
  6. *
  7. * 核心函数式接口 消费型
  8. */
  9. @Test
  10. public void test4(){
  11. happyTime(500, new Consumer<Double>() {
  12. @Override
  13. public void accept(Double aDouble) {
  14. System.out.println("学习累了,去消费了"+aDouble+"元!");
  15. }
  16. });
  17. System.out.println("************************************");
  18. happyTime(100, money -> System.out.println("搬砖辛苦赚了"+money+"元!"));
  19. }
  20. public void happyTime(double money,Consumer<Double> con) {
  21. con.accept(money);
  22. }
  23. /**
  24. * 核心函数式接口 断言型
  25. */
  26. @Test
  27. public void test5(){
  28. List<String> stringList = Arrays.asList("北京","南京","天津","西京","东京");
  29. List<String> strings = filterStr(stringList, new Predicate<String>() {
  30. @Override
  31. public boolean test(String s) {
  32. return s.contains("京");
  33. }
  34. });
  35. strings.stream().forEach(item -> System.out.println(item));
  36. System.out.println("***************************************");
  37. List<String> strings2 = filterStr(stringList, s -> s.contains("京"));
  38. strings2.stream().forEach(item -> System.out.println(item));
  39. }
  40. public List<String> filterStr(List<String> list, Predicate<String> predicate) {
  41. List<String> filter = new ArrayList<>();
  42. list.stream().forEach(str -> {
  43. if(predicate.test(str)) {
  44. filter.add(str);
  45. }
  46. });
  47. return filter;
  48. }

image.png
以上接口都是核心函数式接口的变形。可以灵活应用lambda表达式。

3. 方法引用

image.png

lambda表达式作为函数式接口的抽象方法的一个实例,简化操作。
将new 的形式创建实例对象并重写抽象方法 转变成 lambda表达式

image.png

  1. /**
  2. * 情况一:对象::实例方法
  3. * Consumer中void accept(T t)方法
  4. * PrintStream中的void println(T t)方法
  5. */
  6. @Test
  7. public void method1() {
  8. //lambda
  9. Consumer<String> con1 = s -> System.out.println(s);
  10. con1.accept("北京");
  11. System.out.println("********************************");
  12. //方法引用
  13. PrintStream ps = System.out;
  14. Consumer<String> con2 = ps::println;
  15. con2.accept("beijing");
  16. }
  17. /**
  18. * Supplier<T> 中T get()方法
  19. * Employee 中的 String getName()方法
  20. */
  21. @Test
  22. public void method2() {
  23. Employee emp = new Employee(1010,"Tom",23,5600.5);
  24. Supplier<String> supplier = () -> emp.getName();
  25. System.out.println(supplier.get());
  26. System.out.println("********************************");
  27. Supplier<String> supplier1 = emp::getName;
  28. System.out.println(supplier1.get());
  29. }
  30. /**
  31. * 情况二:类::静态方法
  32. * Comparator中int compare(T t1,T t2)方法
  33. * Integer 中的int compare(T t,U u)方法
  34. */
  35. @Test
  36. public void method3() {
  37. //lambda
  38. Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2);
  39. System.out.println(com1.compare(12,21));
  40. System.out.println("********************************");
  41. //方法引用
  42. Comparator<Integer> com2 = Integer::compare;
  43. System.out.println(com2.compare(20, 02));
  44. }
  45. /**
  46. * Function中的R apply(T t)
  47. * Math中的Long round(Double d)
  48. */
  49. @Test
  50. public void method4() {
  51. Function<Double,Long> func1 = (d) -> Math.round(d);
  52. Long apply = func1.apply(3.14);
  53. System.out.println("apply = " + apply);
  54. System.out.println("********************************");
  55. Function<Double,Long> func2 = Math::round;
  56. System.out.println("apply = " + func2.apply(3.66));
  57. }
  58. /**
  59. * 情况3:类::实例方法(有难度)
  60. * Comparator中的int compare(T t1,T t2)
  61. * String中的 int t1.compareTo(t2)
  62. */
  63. @Test
  64. public void method5() {
  65. Comparator<String> comparator1 = (t1,t2) -> t1.compareTo(t2);
  66. int compare = comparator1.compare("", "123");
  67. System.out.println("compare = " + compare);
  68. System.out.println("***********************************");
  69. Comparator<String> comparator2 = String::compareTo;
  70. int compare1 = comparator2.compare("123", "");
  71. System.out.println("compare1 = " + compare1);
  72. }
  73. /**
  74. * BiPredicate<T,U></>中的boolean test(T t,U u)
  75. * String 中的boolean t.equals(u)
  76. */
  77. @Test
  78. public void method6() {
  79. BiPredicate<String,String> predicate = (t1,t2) -> t1.equals(t2);
  80. System.out.println(predicate.test("asd", "asd"));
  81. System.out.println("***********************************");
  82. BiPredicate<String,String> predicate2 = String::equals;
  83. System.out.println(predicate2.test("asd", "jkl"));
  84. }
  85. /**
  86. * Function<T,R> 中的 R apply(T t)
  87. * Employee 中的String getName()
  88. */
  89. @Test
  90. public void method7() {
  91. Employee employee = new Employee(1004, "123", 18, 2500);
  92. Function<Employee, String> func = t -> t.getName();
  93. String apply = func.apply(employee);
  94. System.out.println("apply = " + apply);
  95. System.out.println("***********************************");
  96. Function<Employee,String> fun2 = Employee::getName;
  97. System.out.println("apply = " + fun2.apply(employee));
  98. }

4.Stream api

4.1Stream

image.png
image.png
Stream到底是什么呢?
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
集合讲的是数据,Stream讲的是计算
注意:

  1. Stream不会自己存储元素
  2. Stream不会改变源对象。相反他们会返回一个持有结果的新Stream
  3. Stream是延迟操作的,意味着这些操作会等到需要结果的时候才执行

    4.2 Stream操作流程

    1614269929(1).jpg
    _

    1.
    Stream关注的是
    对数据的运算,与CPU打交道
    集合关注的是
    数据的存储,与内存打交道
    2.
    Stream不会自己存储元素
    Stream
    不会改变源对象。相反他们会返回一个持有结果的新Stream
    Stream是
    延迟操作的,意味着这些操作会等到需要结果的时候才执行 3.Sream执行流程
    stream实例化 中间操作(过滤,映射,排序) 终止操作
    4.说明
    (1) 一个中间操作链,对数据源的数据进行处理
    (2) 一旦执行中止操作,就执行中间操作链,并产生结果。之后,不好再被使用

1. 创建

image.png

  1. //创建Stream 方式一 通过集合Collection的拓展流
  2. // default Stream<E> stream() 返回一个顺序流 -> 从数据源(集合)中获取数据的时候按照原有顺序逐个获取
  3. // default Stream<E> parallelStream() 返回一个并行流 -> 从数据源(集合)中随机获取数据的时候
  4. @Test
  5. public void test1() {
  6. List<Employee> employees = EmployeeData.getEmployees();
  7. //stream 的串行流
  8. Stream<Employee> stream = employees.stream();
  9. //stream的并行流
  10. Stream<Employee> employeeStream = employees.parallelStream();
  11. }

image.png

  1. //创建Stream 方式二 通过数组工具类 Arrays
  2. // public static <T> Stream<T> stream(T[] array)
  3. // intStream stream(int[] array)
  4. // LongStream stream(long[] array)
  5. // DoubleStream stream(double[] array)
  6. @Test
  7. public void test2() {
  8. int[] arr = {1,2,3,4,5};
  9. IntStream stream = Arrays.stream(arr); //放入int型数组,自动创建IntStream
  10. List<Employee> employees = EmployeeData.getEmployees();
  11. Employee[] arr2 = new Employee[]{employees.get(1),employees.get(2)};
  12. Stream<Employee> stream1 = Arrays.stream(arr2); //放入自定义类型数组 创建一般的流
  13. }

image.png

  1. /**
  2. * 创建的方式三 通过Stream 的 of()
  3. * public static<T> Stream<T> of(T t)
  4. * public static<T> Stream<T> of(T... values)
  5. */
  6. @Test
  7. public void test3() {
  8. Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6);
  9. Stream<String> integerStream2 = Stream.of("a");
  10. }

image.png

  1. /**
  2. * 创建的方式四 创建无限流
  3. * 迭代 public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
  4. * seed 迭代的种子(初始值),f迭代的函数 返回类型和种子的类型一致,对结果进行迭代
  5. * 生成 public static<T> Stream<T> generate(Supplier<T> s)
  6. * s 用于生成的函数
  7. */
  8. @Test
  9. public void test4() {
  10. Stream.iterate(0,t -> t+2).limit(10).forEach(System.out::println);
  11. Stream.generate(Math::random).limit(10).forEach(System.out::println);
  12. }

2.中间操作

image.png
image.png
image.png

3.终止操作

image.png

image.png
image.png
image.png
Collectors
image.png

5.Option类

目前为止 臭名昭著的空指针异常是导致java程序异常的主要原因。为了解决空指针异常,Google公司引入了Guava中的Optional类,通过检查使用空值的方式来防止代码污染。
Optional类(java.util.Optional)是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存unll,标识这个值不存在,现在Optional可以更好的表现这个概念,并且可以避免空指针异常。
doc中描述:这是一个可以为null的容器对象,如果值存在,这isPresent()方法会返回true,调用get()方法返回该对象

  • 创建Optional类对象的方法 ```java public static Optional of(T value) 创建一个Optional实例 value必须非空

public static Optional ofNullable(T value) 创建一个Optional实例 value可以为空

public static Optional empty() 创建一个为空的Optional实例

  1. - 判断optional容器中对象是否包含对象
  2. ```java
  3. //判断是否包含对象
  4. boolean isPresent()
  5. //如果有值就执行Consumer接口的实现代码,并且该值会则为参数传递给它
  6. void ifPresent(Consumer<? super T> consumer)
  • 获取optional容器中的对象:
    1. //如果调用对象包含值 返回该值 否则抛出异常
    2. public T get()
    3. //如果有值 则将其返回,否则返回指定的other对象
    4. public T orElse(T other)
    5. //如果有值 则将其返回,否则返回由Supplier接口实现提供的对象
    6. public T orElseGet(Supplier<? extends T> other)
    7. //如果有值 则将其返回,否则返回由Supplier接口实现提供的异常
    8. public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier)