java学习笔记20(Lambda表达式、函数式编程、流式计算、练习)

11.3

学习内容

Lambda表达式

在一个 Runnable 接口中,只有一个 run() 方法:

public abstract void run();

代码特点:

  • 无参数:不需要任何条件即可执行该方案。
  • 无返回值:该方案不产生任何结果。
  • 代码块(方法体):该方案的具体执行步骤。

同样的语义体现在 Lambda 语法中,要更加简单:

()> System.out.println("多线程任务执行!")
  • 前面的一对小括号即 run 方法的参数(无),代表不需要任何条件;
  • 中间的一个箭头代表将前面的参数传递给后面的代码;
  • 后面的输出语句即业务逻辑代码。

Lambda标准格式

Lambda省去面向对象的条条框框,格式由3个部分组成:

  • 一些参数

  • 一个箭头

  • 一段代码

    即:(参数类型 参数) -> {代码}

格式说明
  • 小括号内的语法与传统方法参数列表一致:无参数则留空;多个参数则用逗号分隔。
  • -> 是新引入的语法格式,代表指向动作。
  • 大括号内的语法与传统方法体要求基本一致。
省略规则

Lambda标准格式的基础上,使用省略写法的规则为:

  1. 小括号内参数的类型可以省略;

  2. 如果小括号内有且仅有一个参,则小括号可以省略;

  3. 如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。

  4. 凡是可以通过上下文推导出来的内容,都可以省略书写。

    可省略的内容:

    1. 参数列表:括号中参数的数据类型可省略
    2. 参数列表:括号中如果只有一个参数,那么类型和 () 都可以省略。
    3. 代码块:如果代码块 {} 中的代码只有一行,那么无论是否有返回值,都可以省略({},return,分号),但他们3个必须一起省略

使用前提

  1. 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。无论是内置接口还是自定义接口,只有当接口中的抽象方法存在且唯一时,才可以使用Lambda
  2. 使用Lambda必须具有上下文推断,也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。

函数式接口

预定义的函数式接口

预定义函数接口 方法名 描述符 分类 用途
Supplier<T> T get() () -> T 供给型 提供1个结果
Consumer<T> void accept(T t) (T) -> void 消费型 消费1个输入
BiConsumer<T, U> void accept(T t, U u) (T, U)-> void 消费型 消费2个输入
Function<T, R> R apply(T t) (T) -> R 映射型 完成类型转换
BiFunction<T, R> R apply(T t, U u) (T, U) -> R 映射型 完成类型转换
UnaryOperator<T> T apply(T t) (T) -> T 映射型 完成类型转换
BinaryOperator<T> T apply(T t, U u) (T, T) 映射型 完成类型转换
Predicate<T> boolean test(T t) (T) -> boolean 预测型 判断输入是否符合预期
Bipredicate<T, U> boolean test(T t, U u) (T,U) -> boolean 预测型 判断输入是否符合预期

工作内容

任务1

尝试自定义几个函数式接口用于之前任务的代码中,并用 Lambda 表达式改造之前的代码。

答:在之前写过的 4 线程用同一个锁的任务基础上修改。

以单个线程为例:

   Thread t1 = new Thread(new Runnable() {
   
            @Override
            public void run() {
   
                synchronized ("lock1") {
   
                    System.out.println("t1 线程开始");
                    try {
   
                        "锁".wait();
                    } catch (InterruptedException e) {
   
                        e.printStackTrace();
                    }
                    System.out.println("t1 线程结束");
                }
            }
        });

线程 t1 中,在 Runnable 中有且仅有一个抽象方法:run(),因此满足Lambda 表达式的省略条件。

因此可以改写成:

        new Thread(() -> {
   
            System.out.println("t1线程开始");
            try {
   
                "lock1".wait();
            } catch (InterruptedException e) {
   
                e.printStackTrace();
            }
            System.out.println("t1 线程结束");
        }).start();

总结&明日计划

  1. 个人体会,Lambda 表达式是很容易眼高手低的知识点。看文档、看别人代码的时候会觉得比较好理解,实际自己写的时候却很容易卡壳,感觉明天还需要自己再把别人的代码多敲几次,在敲的过程中也可以更好的体会 Lambda 的省略原则。
  2. 明日争取把函数式编程和流式编程都搞完

11.4

学习内容

流式计算

Stream,也就是流,也叫做流式计算。利用 Steam ,可以让 java 以声明性地迭代方式处理集合。

元素是特定类型的对象,形成一个队列。 Stream并不会存储元素,而是按需计算。

流的来源可以是集合,数组 等。

两大特征

Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。

内部迭代: 以前对集合遍历都是通过Iterator或者增强for的方式, 显式的在集合外部进行迭代,这叫做外部迭代。 Stream提供了内部迭代的方式,流可以直接调用遍历方法。

两大操作

Java8 的 Stream API 中定义了两大类流式操作,一种是中间操作,一种是终端操作。

所谓中间操作,就是类似于流水线上,给上游工序做一些加工和包装,然后再传给自己的下游工序。同样的道理,中间操作流会返回另一个流对象,这个流对象的数据是需要继续传递给后面的流操作处理的。

终端操作就是真正结果的操作,类似于流水线上最后一道工序。

获取流的方式

  1. 所有的 Collection 集合都可以通过 stream 默认方法获取流;
  2. Stream 接口的静态方法 of 可以获取数组对应的流。

flatMap 方法

使用flatMap方法的效果是,各个数组并不是分别映射一个流,而是映射成流的内容,所有使用生成的单个流被合并起来,即扁平化为一个流。

例如,将流1:{"H","e","l","l","o"} 与流2:{"W","o","r","l","d"} 用普通方式合并,结果为:{"H","e","l","l","o","W","o","r","l","d"}

flatMap 合并,其结果为:{"H","e","l","o","W","r","d"}

工作内容

任务1

除了消费型函数式接口,从搜索引擎或 CSDN 上再分别找一些供给型、映射型和预测型函数式接口的实际应用例子。

package com.xxm.advanced_camp.mission12_FunctionalParadigm;

import java.util.Locale;
import java.util.function.*;

public class TestTask2 {
   
    public static void main(String[] args) {
   
        //供给型
        int arr[] = {
   24, 12, 34, 87, 5};
        int maxNum = getMax(() -> {
   
            int max = arr[0];
            for (int x : arr) {
   
                if (x > max) {
   
                    max = x;
                }
            }
            return max;
        });
        System.out.println("供给型函数接口测试。数组最大值为:" + maxNum);

        //消费型
        printString(s -> System.out.println(s));

        //映射型
        toUpper(s -> s.toUpperCase());

        //预测型
        lenthLongerThanThree(s -> s.length() > 3);

    }

    //供给型,方法
    public static int getMax(Supplier<Integer> supplier) {
   
        return supplier.get();
    }

    //消费型,方法
    public static void printString(Consumer<String> consumer) {
   
        consumer.accept("消费型接口测试:HelloWorld");
    }

    //映射型,方法
    public static String toUpper(UnaryOperator<String> function) {
   
        String s = function.apply("映射型接口测试:HelloWorld");
        System.out.println(s);
        return s.toUpperCase();

    }

    //预测型,方法
    public static void lenthLongerThanThree(Predicate<String> predicate) {
   
        boolean longer = predicate.test("HelloWorld");
        System.out.println("预测型接口测试。字符串 HelloWorld 的长度大于 3 吗?" + longer);
    }
}

输出结果为:

供给型函数接口测试。数组最大值为:87
消费型接口测试:HelloWorld
映射型接口测试:HELLOWORLD
预测型接口测试。字符串 HelloWorld 的长度大于 3 吗?true

任务2

实现如下需求:统计公司类型为 BIG 的所有未婚员工,同时按年龄排序,并且未婚员工不区分公司,也就是最终结果放在一个列表中(提示:整个需求可以用一行代码完成)。

package com.xxm.advanced_camp.mission13_stream;

import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Task1 {
   

    public static void main(String[] args) {
   

        List<Company<Employee>> list = new ArrayList<>();
        List<Employee> employees = Arrays.asList(
                new Employee("张勇", "男", 28, true, Employee.Type.MANAGER),
                new Employee("李强", "男", 22, false, Employee.Type.SELLER),
                new Employee("王武", "男", 32, false, Employee.Type.SELLER),
                new Employee("梅丽", "女", 26, true, Employee.Type.OFFICER),
                new Employee("郑帅", "男", 29, false, Employee.Type.OFFICER),
                new Employee("曾美", "女", 27, true, Employee.Type.SELLER),
                new Employee("郝俊", "男", 22, true, Employee.Type.SELLER),
                new Employee("方圆", "女", 24, false, Employee.Type.SELLER)
        );
        Company<Employee> moubao = new Company<Employee>("某宝", Company.Type.BIG, employees);

        employees = Arrays.asList(

                new Employee("吴琼", "女", 27, true, Employee.Type.SELLER),
                new Employee("陈辰", "女", 20, false, Employee.Type.OFFICER),
                new Employee("刘能", "男", 25, true, Employee.Type.OFFICER),
                new Employee("周七", 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值