Lambda表达式

Lambada是一个匿名函数,作为一种更紧凑的代码风格
特点是函数式接口,接口内只有一个抽象方法才能使用Lambda

(Lambda形参列表) -> Lambda的操作符 {Lambda体};
Lambda体 如果只有一句 大括号和return都可省略
形参列表只有一个并且不指定数据类型 小括号可以省略

Lambda本质就是函数式接口的实例,借助接口存在(java万物皆对象)
使用

// 不使用Lambda表达式
@Test
public void test() {
    Runnable r1 = new Runnable() {
        @Override
        public void run() {
            System.out.println("哈哈哈!");
        }
    };
    r1.run();
}
// 使用Lambda表达式
@Test
public void test2() {
    Runnable r2 = () -> System.out.println("哈哈哈!");
    r2.run();
}

类型推断
编译器通过泛型推断数据类型

@Test
public void test8() {
    // Consumer<String> consumer = (String s) -> System.out.println(s);
    Consumer<String> consumer = s -> System.out.println(s);
    consumer.accept("321");
}

函数式接口
内部只有一个抽象方法,可能还有@FunctionalInterface注解
如果没有该注解,但是只有一个抽象方法,也属于函数式接口
该注解效果类似@Override,只是校验作用

@FunctionalInterface
public interface Functional {
    void method1();
    default void method2() {
        System.out.println("this is functionalInterface!");
    }
}

主要函数式接口

消费型接口 Consumer void accept(T t)
供给型接口 Supplier T get()
函数型接口 Function<T,R> R apply(T t)
断定型接口 Preducate boolean test(T t)

方法引用,构造器引用,数组引用

当要传递给Lambda已有实现方法才可以使用方法引用
类对象与实例对象
接口中的抽象方法的形参列表和返回值需要与Lambda体中的一致

// 类对象调用静态方法
@Test
public void test5() {
    // Comparator<Integer> com1 = (o1, o2) -> Integer.compare(o1, o2);
    Comparator<Integer> com1 = Integer::compare;
    int compare = com1.compare(12, 21);
    System.out.println(compare);
}
// 实例对象调用非静态方法
@Test
public void test9() {
    // Consumer<String> consumer = s -> System.out.println(s);
    // 方便理解拆分为两句
    // Consumer<String> consumer = System.out::println;
    PrintStream out = System.out;
    Consumer<String> consumer = out::println;
    consumer.accept("321");
}

类对象调用实例方法
第一个参数为方法调用者

// 类对象调用非静态方法
@Test
public void test4() {
    // s1是方法调用者的身份存在,这种情况也可以使用方法引用
    // 但是不是以具体对象而是以类(String)调用compareTo
    // Comparator<String> com = (s1, s2) -> s1.compareTo(s2);
    Comparator<String> com = String::compareTo;
    System.out.println(com.compare("abc", "cbd"));
}
@Test
public void test5() {
    Employee employee = new Employee(1001, "Jerry");
    // Function<Employee, String> func = e -> e.getName();
    Function<Employee, String> func = Employee::getName;
    System.out.println(func.apply(employee));
}

构造器引用

@Test
public void test6() {
    // 调用无参构造器 使用供给型接口
    // Supplier<Employee> supplier = () -> new Employee();
    Supplier<Employee> supplier = Employee::new;
    System.out.println(supplier.get());
}
@Test
public void test7() {
    // 调用有参构造器 使用函数型接口
    // Function<Integer, Employee> func = id -> new Employee(id);
    Function<Integer, Employee> func = Employee::new;
    System.out.println(func.apply(2));
}
@Test
public void test8() {
     // BiFunction也是函数式接口不过提供两个形参
     // R apply(T t, U u);
     // BiFunction<Integer, String, Employee> fun = (id, name)
     //        -> new Employee(id, name);
    BiFunction<Integer, String, Employee> func = Employee::new;
    System.out.println(func.apply(50, "jack"));
}

数组引用
与构造器引用类似

@Test
public void test() {
    // Function<Integer, String[]> func = length -> new String[length];
    Function<Integer, String[]> func = String[]::new;
    // 定义一个长度为5的数组
    String[] arr = func.apply(5);
    System.out.println(Arrays.toString(arr));
}

Stream API

一个中间操作连,对数据源数据进行处理
一旦执行了最后一步,就执行中间操作链,并产生结果
每次执行终止操作后,再次使用都需要创建新的stream对象
Stream对象的创建

@Test
public void test() {
    List<Employee> employees = EmployeeData.getEmployee();
    // 返回一个顺序流
    Stream<Employee> stream = employees.stream();
    // 返回一个并行流
    Stream<Employee> parallelStream = employees.parallelStream();
}
@Test
public void test2() {
    // 返回IntStream 
    int[] arr = {1, 2, 3, 4, 5, 6};
    IntStream stream = Arrays.stream(arr);
    // 返回指定泛型的Stream
    Employee employee = new Employee();
    Employee employee2 = new Employee();
    Employee[] arr2 = {employee, employee2};
    Stream<Employee> stream2 = Arrays.stream(arr2);
}
@Test
public void test3() {
    Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
}
//无限流,没有limit将无限循环
@Test
public void test4() {
    // 迭代,最初为0,每次+2
    Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);
    // 生成
     Stream.generate(Math::random).limit(10).forEach(System.out::println);
}

Stream中间操作
筛选与切片

@Test
public void test() {
    List<Employee> list = EmployeeData.getEmployee();
    // 筛选大于7000的元素
    list.stream().filter(e -> e.getSalary() > 7000).forEach(System.out::println);
    // 截取前3个元素
    list.stream().limit(3).forEach(System.out::println);
    // 跳过前3个元素,如果不足则返回空
    list.stream().skip(3).forEach(System.out::println);
    // 通过hashCode和equals方法去重,去除重复数据
    list.stream().distinct().forEach(System.out::println);
}

映射

@Test
public void test2() {
    List<String> list = Arrays.asList("aa", "bb", "cc");
    // 通过某种规则映射为另一个值
    list.stream().map(String::toUpperCase).forEach(System.out::println);
}

map和flatMap

@Test
public void test4() {
    List<String> list = Arrays.asList("aa", "bb", "cc");
    // 方法返回的就是Stream,内部封装了Stream<Character>
    // 所以返回的是Stream<Stream<Character>>
    Stream<Stream<Character>> streamStream = list.stream().map(SteamAPI::fromStringToStream);
    // 因为是Stream<Stream<Character>>,所以需要两次拆开
    streamStream.forEach(e -> {
        e.forEach(System.out::println);
    });
}
@Test
public void test5() {
    List<String> list = Arrays.asList("aa", "bb", "cc");
    // 而flatMap可以一步到位
    // flatMap可以方便的遍历复杂数据类型[1,2,3,[4,5,6]]
    // map和flatMap的区别类似与List的add和addAll
    Stream<Character> cStream = list.stream().flatMap(SteamAPI::fromStringToStream);
    cStream.forEach(System.out::println);
}
public static Stream<Character> fromStringToStream(String str) {
    List<Character> list = new ArrayList<>();
    for (Character c : str.toCharArray())
        list.add(c);
    return list.stream();
}

排序

@Test
public void test6() {
    List<Integer> list = Arrays.asList(12, 43, 65, 34, 87, 0, -98, 7);
    // 默认自然排序,以小到大排序
    list.stream().sorted().forEach(System.out::println);
}
@Test
public void test7() {
    List<Employee> employee = EmployeeData.getEmployee();
    // 自定义排序
    // 提供一个compare方法或者Employee内部实现Comparator接口
    employee.stream().sorted((e1, e2) -> {
                int age = Integer.compare(e1.getAge(), e2.getAge());
                if (age != 0) return age;
                else return Double.compare(e1.getSalary(), e2.getSalary());
            }
    ).forEach(System.out::println);
}

Stream终止操作
匹配与查找
以下部分为一般情况下的最后操作,但是不是终止操作
比如max和min并不会关闭流,但是forEach会关闭流,之后就无法使用

@Test
public void test() {
    List<Employee> employee = EmployeeData.getEmployee();
    // 是否匹配所有元素
    boolean allMatch = employee.stream().allMatch(e -> e.getAge() > 18);
    // 是否至少匹配一个元素
    boolean anyMatch = employee.stream().anyMatch(e -> e.getSalary() > 5000);
    // 是否没有匹配的元素
    boolean noneMatch = employee.stream().noneMatch(e -> e.getName().contains("哈"));
    // 返回Optional类型的第一个元素
    Optional<Employee> firstEmployee = employee.stream().findFirst();
    // 返回Optional类型的任意一个元素
    Optional<Employee> any = employee.stream().findAny();
}
@Test
public void test2() {
    List<Employee> employee = EmployeeData.getEmployee();
    // 返回流中元素的数量
    long employeeStream = employee.stream().count();
    // 通过指定比较,返回Optional类型的最大值
    Optional<Double> max = employee.stream()
            .map(Employee::getSalary)
            .max(Double::compareTo);
    // 通过指定比较,返回Optional类型的最小值
    Optional<Employee> min = employee.stream()
            .min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
    // 内部迭代
    employee.stream().forEach(System.out::println);
}

规约

@Test
public void test3() {
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
    // 计算集合里所有数的和,第一个参数为初始值
    Integer reduce = list.stream().reduce(0, Integer::sum);
    System.out.println(reduce);
    List<Employee> employees = EmployeeData.getEmployee();
    // 返回Optional类型的所有数的和 
    Optional<Double> sum = employees.stream()
        .map(Employee::getSalary)
        .reduce(Double::sum);
    System.out.println(sum);
}

收集

@Test
public void test4() {
    List<Employee> employees = EmployeeData.getEmployee();
    List<Employee> collect = employees.stream()
            .filter(e -> e.getSalary() > 6000)
            // 将stream转换为List集合
            .collect(Collectors.toList());
    Set<Employee> collect2 = employees.stream()
            .filter(e -> e.getSalary() > 6000)
            // 将stream转换为Set
           .collect(Collectors.toSet());
   List<Employee> collect3 = employees.stream()
          .filter(e -> e.getSalary() > 6000)
          // 将stream转换为指定集合
          .collect(Collectors.toCollection(LinkedList::new));
           
}

Optional

一种容器,对数据进行封装,可以避免空指针异常
创建Optional

@Test
public void test() {
    Girl girl = new Girl();
    // 参数必须非空
    Optional<Girl> optionalGirl = Optional.of(girl);
}
@Test
public void test2() {
    // 创建一个空的Optional实例
    Optional<Object> empty = Optional.empty();
}
@Test
public void test3() {
    Girl girl = null;
    // 参数可以为空
    // 源码 return value == null ? empty() : of(value);
    Optional<Girl> optionalGirl = Optional.ofNullable(girl);
}

Optional的使用

@Test
public void test3() {
    Girl girl = new Girl();
    Optional<Girl> optionalGirl = Optional.ofNullable(girl);
    System.out.println(optionalGirl);
    // 如果Optional内封装的Girl为null才会覆盖
    Girl girl2 = optionalGirl.orElse(new Girl("111"));
    System.out.println(girl2);
}
@Test
public void test5() {
    Girl girl = new Girl();
    Optional<Girl> optionalGirl = Optional.ofNullable(girl);
    // 判断是否包含对象
    boolean present = optionalGirl.isPresent();
    System.out.println(present);
    // 从Optional中拿到具体的对象,如果为空将抛出异常
    Girl girl2 = optionalGirl.get();
    // true
    System.out.println(girl2 == girl);
}