【Java8】Lambda表达式 和 Stream API 详解笔记

视频地址:https://www.bilibili.com/video/BV1ut411g7E9
笔记 md 文档:https://download.csdn.net/download/qq_43290318/13192803

文档效果图:
在这里插入图片描述

Lambda表达式

概念

​ Lambda是一个匿名函数,Lambda表达式可以理解为一段可以传递的代码(将代码像数据一样进行传递)

引入

/**
  * 匿名内部类
  */
@Test
public void test1() {
    
    
    Comparator<Integer> com = new Comparator<Integer>() {
    
    
        @Override
        public int compare(Integer o1, Integer o2) {
    
    
            return Integer.compare(o1, o2);
        }
    };
    TreeSet<Integer> ts = new TreeSet<>(com);
}

// 简化1
@Test
public void test1() {
    
    
    Comparator<Integer> com = (o1, o2) -> Integer.compare(o1, o2);
    TreeSet<Integer> ts = new TreeSet<>(com);
}

// 简化2
@Test
public void test1() {
    
    
    Comparator<Integer> com = Integer::compare;
    TreeSet<Integer> ts = new TreeSet<>(com);
}

语法

  1. 箭头操作符 或 Lambda操作符: ->

左侧:Lambda表达式的参数列表

右侧:Lambda表达式中所执行的功能,即 Lambda体

  1. 无参数、无返回值
		Runnable run = () -> System.out.println("Hello, Lambda!");
        run.run();
  1. jdk 1.8 以前,匿名内部类里面引用的外部变量必须有final修饰,但在 jdk 1.8 开始,可省略final,但本质上仍是个final常量
		// final int num = 0;
        int num = 0;
        Runnable run = () -> System.out.println("Hello, Lambda!" + num);
        run.run();
  1. 有 1 个参数,无返回值
		// Consumer<String> consumer = (x) -> System.out.println(x); // 可省略()
		// Consumer<String> consumer = x -> System.out.println(x); 
        Consumer<String> consumer = System.out::println;
        consumer.accept("test");
  1. 有 2 个及以上参数,并且 Lambda 体中有多条语句

单个参数可省略圆括号,多条语句必须要花括号,单条语句可省略花括号,单挑语句且有返回值还可以省略return

		Comparator<Integer> comparator = (x, y) -> {
    
      
            System.out.println(x + " " + y);
            return Integer.compare(x, y);
        };
  1. 参数列表的数据类型可省略

因为JVM可以通过上下文推断出数据类型

四大函数式接口

函数式接口:只有一个抽象方法的接口,可用@FunctionalInterface

消费型接口:Consumer<T>

	// 如何消费数据,无返回值
	// 有 --消费--> 无
	void accept(T t);

供给型接口:Supplier<T>

	// 如何产生数据,返回指定格式的数据
	// 无 --产生--> 有
	T get();
	@Test
    public void test1() {
    
    
        produceRandomArray(10, () -> new Random().nextInt(10));
    }

    private void produceRandomArray(Integer count, Supplier<Integer> sup) {
    
    
        List<Integer> numList = new ArrayList<>();
        for (int i = 0; i < count; i++) {
    
    
            numList.add(sup.get());
        }
        numList.forEach(System.out::println);
    }

函数型接口:Function<T, R>

	// 源数据 --处理--> 新数据
	R apply(T t);

断言型接口:Predicate<T>

	boolean test(T t);

常见子接口

在这里插入图片描述

方法引用

若方法 Lambda 体中的内容已经有现成方法了,换言之, Lambda 表达式的参数列表与返回值类型,与函数式接口抽象方法的函数列表和返回值类型保持一致,那么就可以使用“方法引用”

  1. 对象名::实例方法名
  2. 类::静态方法名
  3. 类::实例方法名

比较特殊,条件更加苛刻! Lambda 表达式参数列表中的第一个参数是实例方法的调用者时,即

	BiPredicate<String, String> biPredicate = (x, y) -> x.contains(y);
	Function<Employee, String> function4 = (e) -> e.getName();

可以使用ClassName :: Method

	BiPredicate<String, String> biPredicate1 = String::contains;
	Function<Employee, String> function5 = Employee::getName;
    // 1. 对象名::实例方法名
    Consumer<String> consumer = System.out::println;
    consumer.accept("test"); 
	
	// 2. 类::静态方法名
	// 相当于用等号左边的现有方法,实现了,等号左侧接口的抽象方法。有点像实现类对象指向抽象类引用
	Comparator<Integer> comparator = Integer::compare;
    BiFunction<Integer, Integer, Integer> biFunction = Integer::compare;
    ToIntBiFunction<Integer, Integer> toIntBiFunction = Integer::compare;

	// 3. 类::实例方法名
	BiPredicate<String, String> biPredicate = (x, y) -> x.contains(y);
	BiPredicate<String, String> biPredicate1 = String::contains;
    BiPredicate<String, String> biPredicate2 = String::equals;
    ToIntBiFunction<String, String> toIntBiFunction1 = String::compareTo;
	
	Function<Employee, String> function4 = (e) -> e.getName();
    Function<Employee, String> function5 = Employee::getName;

构造器引用

类命::new

	// Lambda表达式(或者引用的方法)的参数列表与返回值类型,
    // 要与函数式接口抽象方法的函数列表和返回值类型保持一致

    // 调用无参构造器。因为Supplier的抽象方法是 T get();
    Supplier<Employee> supplier = Employee::new;
    // 调用有参构造器 Employee(String name)
    Function<String, Employee> function = Employee::new;
    // 调用有参构造器 Employee(String name, Integer age)
    BiFunction<String, Integer, Employee> biFunction2 = Employee::new;

数组引用

Type[]::new

	Function<Integer, String[]> function1 = (x) -> new String[x];
    Function<Integer, String[]> function2 = String[]::new;
    String[] strArr = function2.apply(10);

Stream API

什么是Stream

在这里插入图片描述

Stream的 3 个操作步骤

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

创建Stream

    // 1. 通过Collection系列集合的 stream() 方法或 parallelStream()
    List<String> list = new ArrayList<>();
    Stream<String> stream = list.stream();

    // 2. 通过 Arrays 类中的静态方法 stream() 获取数组流
    String[] strArr = new String[10];
    Stream<String> stream1 = Arrays.stream(strArr);

    // 3. 通过 Stream 类中的静态方法 of()
    Stream<String> stream2 = Stream.of("aa", "bb", "cc");

    // 4. 创建无限流,要有终止操作才有效果
    // (1)迭代
    Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2);
    stream3.forEach(System.out::println); // 不停打印,停不下来
    stream3.limit(10) // 中间操作
        .forEach(System.out::println); // 终止操作
    // (2)生成
    Stream.generate(() -> new Random().nextInt(32))
        .limit(32)
        .forEach(System.out::println);

中间操作

筛选和切片

在这里插入图片描述

映射

在这里插入图片描述

    List<Employee> emps = Arrays.asList(
                new Employee("张三", 18, 9999.9),
                new Employee("李四", 22, 6666.9),
                new Employee("王五", 23, 8888.9),
                new Employee("赵六", 26, 7777.9),
                new Employee("田七", 20, 5555.9)
                );
    emps.stream()
        .map(Employee::getName)
        .forEach(System.out::println);

	String[] nums = {
    
     "10.2", "1.34", "3.14" };
    List<String> list = Arrays.asList(nums);
    DoubleStream doubleStream = list.stream()
        .mapToDouble(Double::valueOf);

map()和flatMap()的区别

在这里插入图片描述
在这里插入图片描述

	List<String> list = Arrays.asList("aaa", "bbb", "ccc");
    // map()本身返回Stream
    // Test1::apartStr返回的是Stream<Character>
    Stream<Stream<Character>> streamStream = list.stream()
        .map(Test1::apartStr);

    streamStream.forEach((stream) -> {
    
    
        stream.forEach(System.out::println);
    });

    // ------------

    Stream<Character> charStream =
        list.stream().flatMap(Test1::apartStr);
    charStream.forEach(System.out::println);

	List<String> list = Arrays.asList("m-k-l-a", "1-3-5-7");
    List<String> listNew = list.stream().flatMap(s -> {
    
    
        // 将每个元素转换成一个stream
        String[] split = s.split("-");
        Stream<String> s2 = Arrays.stream(split);
        return s2;
    }).collect(Collectors.toList());

    System.out.println("处理前的集合:" + list);
    System.out.println("处理后的集合:" + listNew);

排序

在这里插入图片描述

自然排序:Comparable

定制排序:Comparator

	List<String> list = Arrays.asList("aaa", "ccc", "bbb");
    list.stream()
        .sorted()
        .forEach(System.out::println);

    list.stream()
        .sorted((s1, s2) -> {
    
    
            return -s1.compareTo(s2);
        })
        .forEach(System.out::println);

终止操作

查找和匹配

  • allMatch:检查是否匹配所有元素
  • anyMatch:检查是否至少匹配一个元素
  • noneMatch:检查是否没有匹配所有元素
  • findFirst:返回第一个元素
  • findAny:返回当前流中的任意元素
  • count:返回流中元素的总个数
  • max:返回流中最大值
  • min:返回流中最小值
    List<Employee> emps = Arrays.asList(
        new Employee("张三", 18, 9999.9, Employee.Status.FREE),
        new Employee("李四", 22, 6666.9, Employee.Status.WORK),
        new Employee("王五", 23, 8888.9, Employee.Status.PLAY),
        new Employee("赵六", 26, 7777.9, Employee.Status.FREE),
        new Employee("田七", 20, 5555.9, Employee.Status.FREE)
    );

    // 全部 Employee 是 Free,就会返回true
    boolean flag = emps.stream()
        .allMatch((e) -> e.getStatus().equals(Employee.Status.FREE));
    System.out.println(flag);

    // 只要有 1 个 Employee 是 Free,就会返回true
    boolean flag1 = emps.stream()
        .anyMatch((e) -> e.getStatus().equals(Employee.Status.FREE));
    System.out.println(flag1);

    // 全部 Employee 都不是 Free,就会返回 true
    // 只要有一个 Employee 是 Free,就会返回 false
    boolean flag2 = emps.stream()
        .noneMatch((e) -> e.getStatus().equals(Employee.Status.FREE));
    System.out.println(flag2);

    Optional<Employee> emp1 = emps.stream()
        .findFirst();
    //        System.out.println(emp1.get());

    // 如果是数据较少,串行地情况下,一般会返回第一个结果
    // 如果是并行的情况,那就不能确保是第一个。
    Optional<Employee> emp2 = emps.parallelStream() // 并行流
        .filter((e) -> e.getStatus().equals(Employee.Status.FREE))
        .findAny();
    System.out.println(emp2.get());

    // 
    Optional<Employee> maxEmp = emps.stream()
        .max(Employee::compareTo);
    System.out.println(maxEmp.get());

    // 求最高工资
    Optional<Double> maxSalary = emps.stream()
        .map(Employee::getSalary)
        .max(Double::compareTo);
    System.out.println(maxSalary.get());

归约

在这里插入图片描述

	// 求和
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

    // Integer::sum  ----> (x, y) -> x+y
    // 一开始把 0 当作 x,然后从集合中取出一个元素当作 y,求和为 1
    // 然后把 1 再当作 x,再从集合中取出一个元素当作 y,求和为 3
    // ...
    Integer sum = list.stream()
        // 有0作为初始值,不可能为空
        .reduce(0, Integer::sum); 
    System.out.println(sum);


    Optional<Integer> sum1 = list.stream()
        // 没有初始值,可能为空,所以返回 Optional 对象
        .reduce(Integer::sum);
    System.out.println(sum1.get());

收集

在这里插入图片描述
在这里插入图片描述

    // 拼接字符串
	// reduce也可以实现拼接字符串,自行尝试
	List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
    String collect = list.stream()
        .map(String::valueOf)
        .collect(Collectors.joining("-", "[", "]"));
    System.out.println(collect);
    // [1-2-3-4-5]

	// 收集成指定集合
	HashSet<String> collect1 = list.stream()
            .map(String::valueOf)
            .collect(Collectors.toCollection(HashSet::new));
    System.out.println(collect1);
	
	// 求平均值
	Double avgSalary = emps.stream()
            .collect(Collectors.averagingDouble(Employee::getSalary));
    System.out.println(avgSalary);

	// 求和的3种方式
	double sum = emps.stream()
            .mapToDouble(Employee::getSalary)
            .sum();
    double sum2 = emps.stream()
        .mapToDouble(Employee::getSalary)
        .reduce(0, Double::sum);
    double sum3 = emps.stream()
        .collect(Collectors.summingDouble(Employee::getSalary));

	// 最大值、最小值,也可以收集
	// 举一反三。。。略

	// 一级分组
    Map<Integer, List<Employee>> map = emps.stream()
        .collect(Collectors.groupingBy(Employee::getStatus));
    System.out.println(map);

    // 多级分组
    Map<Integer, Map<String, List<Employee>>> mapMap = emps.stream()
        .collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
    
    
            // 返回值作为第二层Map的key
            if (e.getAge() > 35) {
    
    
                return "开除";
            } else {
    
    
                return "继续加班";
            }
        })));
    System.out.println(mapMap);
    
    // 分区(分成两部分)
    Map<Boolean, List<Employee>> listMap = emps.stream()
        .collect(Collectors.partitioningBy((e) -> e.getSalary() > 6000));
    System.out.println(listMap);
	
	//总结(方便地获取多种数据统计方式的结果)
    DoubleSummaryStatistics dss = emps.stream()
        .collect(Collectors.summarizingDouble(Employee::getSalary));
    System.out.println(dss.getMax());
    System.out.println(dss.getMin());
    System.out.println(dss.getSum());
    System.out.println(dss.getCount());
    System.out.println(dss.getAverage());

并行流和顺序流

概念理解

streamparallelStream的简单区分: stream是顺序流,由主线程按顺序对流执行操作,而parallelStream是并行流,内部以多线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求。如果流中的数据量足够大,并行流可以加快处速度。

例如筛选集合中的奇数,两者的处理不同之处:

在这里插入图片描述

在这里插入图片描述

了解Fork/Join框架

在这里插入图片描述

在这里插入图片描述

利用Fork/Join的API计算和

public class ForkJoinCalculate extends RecursiveTask<Long> {
    
    

    private static final long serialVersionUID = 1234567890L;

    private long start;
    private long end;

    private static final long THRESHPLD = 10000;

    public ForkJoinCalculate(long start, long end) {
    
    
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
    
    
        long length = end - start;

        if (length <= THRESHPLD) {
    
    
            long sum = 0;
            for (long i = start; i <= end; i++) {
    
    
                sum += i;
            }
        } else {
    
    
            long middle = (start + end) / 2;

            ForkJoinCalculate left = new ForkJoinCalculate(start, end);
            left.fork(); //拆分子任务 压入线程队列

            ForkJoinCalculate right = new ForkJoinCalculate(middle + 1, end);
            right.fork();

            return left.join() + right.join();
        }

        return null;
    }
}

public class TestForkJoin {
    
    

    /**
     * ForkJoin 框架
     */
    @Test
    public void test01(){
    
    
        Instant start = Instant.now();

        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinCalculate task = new ForkJoinCalculate(0, 100000000L);

        Long sum = pool.invoke(task);
        System.out.println(sum);

        Instant end = Instant.now();
        System.out.println(Duration.between(start, end).getNano());
    }

    /**
     * 普通 for循环
     */
    @Test
    public void test02(){
    
    
        Instant start = Instant.now();

        Long sum = 0L;
        for (long i = 0; i < 100000000L; i++) {
    
    
            sum += i;
        }

        Instant end = Instant.now();
        System.out.println(Duration.between(start, end).getNano());
    }
}

Java8并行流例子

	//串行流(单线程):切换为并行流 parallel()
    //并行流:切换为串行流 sequential()
	// 结果会溢出,不需要理会。运行时,注意观察CPU状态。运算会比普通for省时
    LongStream.rangeClosed(0, 100000000L) 
        .parallel() //底层:ForkJoin
        .reduce(0, Long::sum); 

	// 如果是数据较少,串行地情况下,一般会返回第一个结果
    // 如果是并行的情况,那就不能确保是第一个。
	// 注意,使用并行流时要考虑,并行操作对结果是否有影响
    Optional<Employee> emp2 = emps.parallelStream() // 并行流
        .filter((e) -> e.getStatus().equals(Employee.Status.FREE))
        .findAny();
    System.out.println(emp2.get());

Optional类

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

常用方法:

  • Optional.of(T t):创建一个 Optional 实例
  • Optional.empty(T t):创建一个空的 Optional 实例
  • Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则空实例
  • isPresent():判断是否包含某值
  • orElse(T t):如果调用对象包含值,返回该值,否则返回 t
  • orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回 s 获取的值
  • map(Function f):如果有值对其处理,并返回处理后的 Optional,否则返回 Optional.empty()
  • flatmap(Function mapper):与 map 相似,要求返回值必须是 Optional

接口中的默认方法和静态方法

默认方法

public interface MyFun {
    
    

    default String getName(){
    
    
        return "interface";
    }
}

public class ParentClass {
    
    
    public String getName() {
    
    
        return "ParentClass";
    }
}

public class SubClass extends ParentClass implements MyFun {
    
    
    
}

// 测试调用
@Test
public void test() {
    
    
    SubClass sub = new SubClass();
    sub.getName(); // 输出 ParentClass
}

在这里插入图片描述

静态方法

接口中可以定义静态方法

猜你喜欢

转载自blog.csdn.net/qq_43290318/article/details/110194444