目录

java8新特性

1 lambda表达式

1.1 定义

Lambda 是一个匿名函数,本质上是函数式接口的实例。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。

1.2 细节

  1. 如果方法体中只有1条语句,可以省略{},如果是return语句,必须同时省略return和{}
  2. 如果参数列表只有一个参数,可以省略();
  3. 类型推断:参数列表中可以只写变量名,省略数据类型;
  4. 使用前提:当需要对一个函数式接口实例化时,才可以使用lambda表达式

1.3 格式

-> : lambda操作符或者箭头操作符;
操作符左边:Lambda形参列表(接口中抽象方法的形参列表);
操作符右边:Lambda体(重写的抽象方法的方法体)
在这里插入图片描述

1.3.1 无参无返回值

  Runnable r = new Runnable() {
      @Override
      public void run() {
          System.out.println(111);
      }
  };
  r.run();

如果方法体中只有1条语句,可以不用带{};

 Runnable r1 = () -> {
            System.out.println(2222);
            System.out.println("1111");
        };
        r1.run();

1.3.2 有参无返回值

        Consumer<String> c = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        c.accept("123");

如果只有一个参数,可以不写();

        Consumer<String> c1 = s -> System.out.println(s);
        c1.accept("456");

1.3.3 多个参数、多条语句且有返回值

        Comparator<Integer> c = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                System.out.println(o1);
                System.out.println(o2);
                return Integer.compare(o1, o2);
            }
        };
        int compare = c.compare(10, 8);
        System.out.println(compare);
        Comparator<Integer> c1 = (o1, o2) -> {
            System.out.println(o1);
            System.out.println(o2);
            return Integer.compare(o1, o2);
        };
        System.out.println(c1.compare(8, 9));
        Comparator<Integer> c1 = (o1, o2) -> {
            return Integer.compare(o1, o2);
        };

大括号与return必须同时去除

Comparator<Integer> c1 = (o1, o2) ->  Integer.compare(o1, o2);

2 函数式接口

2.1 定义

只包含一个抽象方法的接口称为函数式接口。

2.2 自定义函数式接口

@FunctionalInterface是函数式接口的注解,编译时可以检查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。

@FunctionalInterface
public interface MyFunctional {
    void method();
}

2.3 与lambda表达式的关系

  1. 在Java8中,Lambda表达式就是一个函数式接口的实例。这就是Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口的实例,那么该对象就可以用Lambda表达式来表示。
  2. 所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。

2.4 作为参数传递的lambda表达式

    @Test
    public void test3() {
        method1(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        }, "abc");
        System.out.println("----------------------------");
        method1(s -> System.out.println(s), "cde");
    }

    public void method1(Consumer<String> c, String str) {
        c.accept(str);
    }

2.5 Java 内置四大核心函数式接口

在这里插入图片描述

2.6 使用场景

在开发中,当我们需要定义一个函数式接口时,首先可以看一下jdk中提供的函数式接口是否满足我们的需求,如果满足,直接调用即可。

3 方法引用、构造器引用、数组引用

java学习笔记8 — java新特性-小白菜博客
如果不熟悉方法引用,可以使用lambda表达式。

在这里插入图片描述

4 Stream API

4.1 概述

  1. Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。

4.2 Stream与Collection的区别

Stream是有关数据计算的,是面向cpu的,通过cpu实现计算;
Collection是静态的内存数据结构,面向内存,用内存存储数据。

4.3 细节

  1. Stream类似于迭代器,本身不存储元素;
  2. Stream不会改变源对象,它们会返回一个带有结果的新Stream;
  3. Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行;

4.4 三个步骤

  1. 实例化Stream对象;
  2. 中间操作(过滤、映射…);
  3. 终止操作,一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用。
    在这里插入图片描述

4.5 Stream的实例化

4.5.1 通过集合

Java8 中的 Collection 接口被扩展,提供了两个获取流的方法:
default Stream stream() : 返回一个顺序流
default Stream parallelStream() : 返回一个并行流

   List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
    list.add(4);
    list.add(5);
    Stream<Integer> stream = list.stream();
    Stream<Integer> integerStream = list.parallelStream();

4.5.2 通过数组

Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:
static Stream stream(T[] array): 返回一个流

   int[] arr = {1, 2, 3, 4, 5};
   IntStream stream = Arrays.stream(arr);

   String[] strs = {"a", "b"};
   Stream<String> stream1 = Arrays.stream(strs);

4.5.3 通过Stream的静态方法of

public static Stream of(T… values) : 返回一个流

Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5);

4.5.4 创建无限流

可以使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流。
迭代:public static Stream iterate(final T seed, final UnaryOperator f)
生成:public static Stream generate(Supplier s)

4.6 Stream的中间操作

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。

4.6.1 切片

在这里插入图片描述

//        filter:筛选
        List<Employee> employees = EmployeeData.getEmployees();
        Stream<Employee> stream = employees.stream();
        stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println);
        System.out.println();
//        limit:截取前n个元素
        employees.stream().limit(4).forEach(System.out::println);
        System.out.println();
//        skip:跳过前n个元素
        employees.stream().skip(3).forEach(System.out::println);
        System.out.println();
//       distinct:根据流所生成的hashcode和equal去除重复元素
        employees.stream().distinct().forEach(System.out::println);

4.6.2 映射

使用映射可以获取流中对象的某个属性,然后使用规约来计算所以对象属性的和。
在这里插入图片描述

  1. map
输出所有员工姓名长度大于3的姓名:先映射获取所有员工的姓名,然后再过滤名字长度小于等于3EmployeeData.getEmployees().stream().map(str -> str.getName()).filter(str -> str.length() > 3).forEach(System.out::println);
  1. flatMap

flatMap(Function f) 相当于List中的addAll方法,addAll是将另一个集合拆分后添加到当前集合中。如果flatMap中的参数是一个Stream,那么会将这个流拆分后添加到当前流中。

        List list = new ArrayList();
        list.add(1);
        list.add(2);
        list.add(3);
//        list.add(Arrays.asList(4, 5, 6));[1, 2, 3, [4, 5, 6]]
        list.addAll(Arrays.asList(4, 5, 6));[1, 2, 3, 4, 5, 6]
        System.out.println(list);
    public void test10(){
        List<String> list = Arrays.asList("aa","bb","cc");

//        使用map:返回值是Stream中嵌套Stream,需要使用双层嵌套去遍历,相当于[[a,a],[b,b],[c,c]]
        Stream<Stream<Character>> streamStream = list.stream().map(this::getCharacter);
        streamStream.forEach(s -> s.forEach(System.out::println));
//      使用flatMap:返回值是Stream,直接遍历即可,相当于[a,a,b,b,c,c]
        Stream<Character> characterStream = list.stream().flatMap(this::getCharacter);
        list.stream().flatMap(this::getCharacter).forEach(System.out::print);
    }
    public Stream<Character> getCharacter(String str){
        Character[] c = new Character[str.length()];
        for (int i = 0; i < str.length(); i++) {
            c[i] = str.charAt(i);
        }
        return Arrays.stream(c);
    }

4.6.3 排序

在这里插入图片描述

  1. 自然排序要求生成Stream流的元素必须实现Comparable接口;
  2. 定制排序
定制排序,需要实现Compactor接口,此处使用方法引用
    public void test11() {
        List<Integer> list = Arrays.asList(5, 3, 1, 7, 9);
        下面两种输出方式相同,只是lambda表达式可以输出更多内容
        list.stream().sorted((Integer::compare)).forEach(o -> System.out.println(o + " "));1 3 5 7 9 
        list.stream().sorted((Integer::compare)).forEach(System.out::print); 13579
    }
EmployeeData中的元素进行排序,先按年龄进行排序,年龄相同按薪资排序
        Stream<Employee> stream = EmployeeData.getEmployees().stream();
        stream.sorted((o1, o2) -> {
            int res = Integer.compare(o1.getAge(), o2.getAge());
            if (res != 0) {
                return res;
            } else {
                return Double.compare(o1.getSalary(), o2.getSalary());
            }
        }).forEach(System.out::println);

4.7 Stream的终止操作

4.7.1 细节

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

4.7.2 匹配

在这里插入图片描述

练习:检查所有员工的年龄是否都大于18
Stream<Employee> stream = EmployeeData.getEmployees().stream();
boolean b = stream.allMatch(employee -> employee.getAge() > 18);

4.7.3 查找

在这里插入图片描述

4.7.4 规约

在这里插入图片描述

    public void test12() {
//        规约操作 reduce
//        练习1:计算自然数1-10的和
        List<Integer> list = new ArrayList();
        for (int i = 1; i < 11; i++) {
            list.add(i);
        }
//        第一个参数是初始值,第二个参数是运算规则
        Integer reduce = list.stream().reduce(0, Integer::sum);
        System.out.println(reduce);
    }
//        计算公司中所有员工的工资总和
        Stream<Employee> stream = EmployeeData.getEmployees().stream();
//        先使用map获取所有员工的工资,然后使用reduce将其累加
        Optional<Double> reduce = stream.map(employee -> employee.getSalary()).reduce(Double::sum);
        System.out.println(reduce);

4.7.5 收集

java学习笔记8 — java新特性-小白菜博客
Collector 接口中方法的实现决定了如何对流执行收集的操作,也就是将stream流转换成其他数据结构(如收集到 List、Set、Map)。
另外, Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:
java学习笔记8 — java新特性-小白菜博客
在这里插入图片描述

//        练习:查找工资大于6000的员工,结果返回一个List或Set
        Stream<Employee> stream = EmployeeData.getEmployees().stream();
        List<Employee> collect = stream.filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
        Set<Employee> set = stream.filter(e -> e.getSalary() > 6000).collect(Collectors.toSet());

5 Optional类

5.1 定义

Optional 类(java.util.Optional) 是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。

5.2 作用

为了避免空指针异常,使用Optional,不需要再显式判断对象是否为null。

5.3 实例化Optional对象的三种方式

  1. Optional.of(T t):t不能为null;
  2. Optional.empty():创建一个空的Optional实例;
  3. Optional.ofNullable(T t):t可以为null;

5.4 获取Optional容器的对象

  1. T get(): 如果调用对象不为空,返回该值,否则抛异常。
  2. T orElse(T other)如果有值则将其返回,否则返回指定的other对象。(一般使用此方法来避免空指针异常)
  3. T orElseGet(Supplier<? extends T> other) :如果有值则将其返回,否则返回由Supplier接口实现提供的对象。
  4. TorElseThrow(Supplier<? extends X> exceptionSupplier) :如果有值则将其返回,否则抛出由Supplier接口实现提供的异常。

5.5 判断Optional容器中是否包含对象

  1. boolean isPresent() : 判断是否包含对象。
  2. void ifPresent(Consumer<? super T> consumer) :如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它。
    在这里插入图片描述

5.6 实例

        Optional<Object> o = Optional.of("123");
        System.out.println(o);
        Optional<Object> empty = Optional.empty();
        System.out.println(empty);
        
        o1中的对象为空,所以返回指定值“123Optional<Object> o1 = Optional.ofNullable(null);
        Object o2 = o1.orElse("123");
        System.out.println(o2); // 123