一篇博客带你学会Lambda(Stream + Optional、后端必知必会)


本文章不多说明理论,主要是汇总。

来学习函数式编程就说明java基础都是比较扎实的,基本一看方法就懂。

匿名内部类

这个是所有lambda写法的前提,所以放在前面了,基本大家都会用。

传统写法:

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        }).start();

lambda:

        new Thread(() -> {
            System.out.println("hello");
        }).start();

转换说明:

在这里插入图片描述

带有此注解的都可用匿名内部类写法。其实就是参数是接口且只有一个抽象方法需要重新。

简单理解:这种参数唯一,方法唯一都是可以推导出来的,所以可以简化。

当然还有一写更简化的,比如只有只有一个参数可以省略(),只有一行代码可以省略{}和return;不用特别记忆,alt + enter可以自动优化,用着用着就会了。

Stream流

创建流

相当于放到流水线上。

list:

List<Student> list = getList();
list.stream();

arr:

int[] arr = new int[10];
Arrays.stream(arr);

map:

Map<Integer, Integer> map = new HashMap<>();
map.entrySet().stream();

中间处理

去重:

.distinct() 

过滤:

.filter()

转换:

.map()

排序:

.sorted()

保留长度:

.limit()

跳过前n个,返回剩余:

.skip()

一转多:

.flalMap()

这个解释一下,其实就是把是集合的属性转换为流。

终结操作

遍历:

.forEach()

计数:

.count()

max & min:

.min()
.max()

转换为集合:

.collect()

任意匹配:

.anyMatch()

全部匹配:

.allMatch()

返回任意一个:

.findAny()

返回第一个:

.findFirst()

归并(累计计算,例如求和):

.reduce()

高级用法

正常map中处理会使用包装类,所以对于基本类型会有装箱拆箱,数据多了会有时间小号。

所以有如下方法优化直接使用基本类:

.mapToInt()
.mapToLong()
.mapToDouble()
.flatMapToInt()
.flatMapToDouble()

并行流:

.parallel()

把串行变为并行处理。

调试:

.peek()

和forEach()相似,但不会终结操作,所以作为调试用。

例子

代码:

package LeetCode.year2024.test11.study.test1;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

class Employee {
    private int id;
    private String name;
    private double salary;

    public Employee(int id, String name, double salary) {
        this.id = id;
        this.name = name;
        this.salary = salary;
    }

    public int getId() {
        return id;
    }

    public String getName() {        return name;
    }

    public double getSalary() {
        return salary;
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建员工列表
        List<Employee> employeeList = new ArrayList<>();
        employeeList.add(new Employee(1, "张三", 5000.0));
        employeeList.add(new Employee(2, "李四", 6000.0));
        employeeList.add(new Employee(3, "王五", 5500.0));

        // 1. 创建流示例
        // 从列表创建流
        List<Employee> list = employeeList;
        Stream<Employee> streamFromList = list.stream();

        // 从数组创建流(这里简单模拟一个整数数组)
        int[] arr = IntStream.range(1, 10).toArray();
        IntStream streamFromArr = Arrays.stream(arr);

        // 从Map创建流(这里以员工编号为键,工资为值构建一个简单Map)
        Map<Integer, Double> salaryMap = new HashMap<>();
        for (Employee employee : employeeList) {
            salaryMap.put(employee.getId(), employee.getSalary());
        }
        Stream<Map.Entry<Integer, Double>> streamFromMap = salaryMap.entrySet().stream();

        // 2. 中间处理操作示例
        // 去重(假设根据员工姓名去重,先简单改造下Employee类重写equals和hashCode方法基于姓名判断)
        List<Employee> distinctEmployees = employeeList.stream()
                .distinct()
                .collect(Collectors.toList());

        // 过滤出工资大于5500的员工
        List<Employee> filteredEmployees = distinctEmployees.stream()
                .filter(employee -> employee.getSalary() > 5500)
                .collect(Collectors.toList());

        // 转换操作,提取员工姓名列表
        List<String> employeeNames = filteredEmployees.stream()
                .map(Employee::getName)
                .collect(Collectors.toList());

        // 排序操作,按照工资从小到大对员工进行排序
        List<Employee> sortedEmployees = filteredEmployees.stream()
                .sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
                .collect(Collectors.toList());

        // 保留长度,只取前2个员工
        List<Employee> limitedEmployees = sortedEmployees.stream()
                .limit(2)
                .collect(Collectors.toList());

        // 跳过前1个员工,返回剩余的
        List<Employee> skippedEmployees = sortedEmployees.stream()
                .skip(1)
                .collect(Collectors.toList());

        // 一转多操作,假设每个员工有多个技能,用字符串数组表示,这里简单模拟下
        List<String> allSkills = employeeList.stream()
                .flatMap(employee -> {
                    String[] skills = {"编程", "沟通"};
                    return java.util.Arrays.stream(skills);
                })
                .collect(Collectors.toList());

        // 3. 终结操作示例
        // 遍历员工姓名列表并打印
        employeeNames.forEach(System.out::println);

        // 计数,统计过滤后员工的数量
        long count = filteredEmployees.stream().count();
        System.out.println("符合条件的员工数量: " + count);

        // 求工资的最大值和最小值
        Optional<Double> maxSalary = sortedEmployees.stream().map(Employee::getSalary).max(Double::compare);
        Optional<Double> minSalary = sortedEmployees.stream().map(Employee::getSalary).min(Double::compare);
        System.out.println("最高工资: " + maxSalary.orElse(0.0));
        System.out.println("最低工资: " + minSalary.orElse(0.0));

        // 转换为集合,将排序后的员工列表转换为新的列表(这里其实就是演示collect的用法,和之前类似)
        List<Employee> collectedEmployees = sortedEmployees.stream().collect(Collectors.toList());

        // 任意匹配,判断是否有工资大于6500的员工
        boolean anyMatch = employeeList.stream().anyMatch(employee -> employee.getSalary() > 6500);
        System.out.println("是否有工资大于6500的员工: " + anyMatch);

        // 全部匹配,判断是否所有员工工资都大于4000
        boolean allMatch = employeeList.stream().allMatch(employee -> employee.getSalary() > 4000);
        System.out.println("是否所有员工工资都大于4000: " + allMatch);

        // 返回任意一个员工(这里从去重后的员工列表中返回任意一个)
        Optional<Employee> anyEmployee = distinctEmployees.stream().findAny();
        if (anyEmployee.isPresent()) {
            System.out.println("任意一个员工: " + anyEmployee.get().getName());
        }

        // 返回第一个员工(这里从去重后的员工列表中返回第一个)
        Optional<Employee> firstEmployee = distinctEmployees.stream().findFirst();
        if (firstEmployee.isPresent()) {
            System.out.println("第一个员工: " + firstEmployee.get().getName());
        }

        // 归并操作,计算所有员工工资总和
        Optional<Double> totalSalary = employeeList.stream().map(Employee::getSalary).reduce(Double::sum);
        System.out.println("员工工资总和: " + totalSalary.orElse(0.0));

        // 4. 高级用法示例
        // 使用mapToDouble优化基本类型操作,计算员工工资平均值(避免装箱拆箱)
        double averageSalary = employeeList.stream().mapToDouble(Employee::getSalary).average().orElse(0.0);
        System.out.println("员工平均工资: " + averageSalary);

        // 并行流示例,使用并行流快速计算工资总和(模拟大数据量下并行处理优势,这里只是简单示意)
        Optional<Double> parallelTotalSalary = employeeList.parallelStream().map(Employee::getSalary).reduce(Double::sum);
        System.out.println("并行流计算员工工资总和: " + parallelTotalSalary.orElse(0.0));

        // 调试示例,使用peek查看过滤后员工的相关信息(不会终结流操作)
        filteredEmployees.stream().peek(employee -> System.out.println("过滤后员工信息: " + employee.getName() + ", " + employee.getSalary()))
                .collect(Collectors.toList());
    }
}

结果:

李四
符合条件的员工数量: 1
最高工资: 6000.0
最低工资: 6000.0
是否有工资大于6500的员工: false
是否所有员工工资都大于4000: true
任意一个员工: 张三
第一个员工: 张三
员工工资总和: 16500.0
员工平均工资: 5500.0
并行流计算员工工资总和: 16500.0
过滤后员工信息: 李四, 6000.0

Optional

创建

创建空Optional对象:

Optional.empty()

必须非空:

Optional.of(object)

可以为空:

Optional.ofNullable(object)

消费or判断

判断是否存在对象并执行:

.isPresent()

获取值

有值返回,否则报错:

.get()

有值返回,否则返回other:

.orElse(other)

有值返回,否则返回Supplier接口实现的对象:

.orElseGet(Supplier<? extends T> other)

如果有值则将其返回,否则抛出由Supplier接口的异常:

.orElseThrow(Supplier<? extends X> exceptionSupplier) :

处理

过滤

如果不符合条件则Optional从有对象变为无对象:

.filter()

转换

普通转换:

.map()

一转多:

.flatMap()

与Stream流用法相似。

注意事项

注意:filter和map、flatMap都会先判断是否为null才执行传入的函数!!!!!

例子

代码:

package LeetCode.year2024.test11.study.test2;
import java.util.Optional;
import java.util.function.Supplier;

class Book {
    private int id;
    private String title;
    private String author;

    public Book(int id, String title, String author) {
        this.id = id;
        this.title = title;
        this.author = author;
    }

    public int getId() {
        return id;
    }

    public String getTitle() {
        return title;
    }

    public String getAuthor() {
        return author;
    }
}

public class OptionalExample {
    public static void main(String[] args) {
        // 1. 创建Optional对象示例

        // 创建空的Optional对象
        Optional<Book> emptyOptional = Optional.empty();

        // 创建必须非空的Optional对象,这里先正常创建一个Book实例
        Book book1 = new Book(1, "Java核心技术", "Cay Horstmann");
        Optional<Book> nonNullOptional = Optional.of(book1);

        // 创建可以为空的Optional对象,先模拟一个可能为空的情况(比如从数据库查询没查到书)
        Book maybeNullBook = null;
        Optional<Book> nullableOptional = Optional.ofNullable(maybeNullBook);

        // 2. 消费或判断操作示例

        // 使用.isPresent()判断Optional中是否有对象,并执行相应操作
        nonNullOptional.ifPresent(book -> System.out.println("存在图书: " + book.getTitle()));
        nullableOptional.ifPresent(book -> System.out.println("存在图书(可能为空的情况): " + book.getTitle()));

        // 3. 获取值操作示例

        // 使用.get()获取值,如果Optional为空则会抛出NoSuchElementException异常
        try {
            Book retrievedBook1 = nonNullOptional.get();
            System.out.println("通过.get()获取到的图书: " + retrievedBook1.getTitle());
            // 下面这行代码会抛出异常,因为emptyOptional是空的
            Book retrievedBook2 = emptyOptional.get();
        } catch (Exception e) {
            System.out.println("获取空的Optional对象的值时出错: " + e.getMessage());
        }

        // 使用.orElse()获取值,如果Optional为空则返回指定的其他对象
        Book defaultBook = new Book(0, "默认图书", "未知作者");
        Book retrievedBook3 = nullableOptional.orElse(defaultBook);
        System.out.println("通过.orElse()获取到的图书(可能为空的情况): " + retrievedBook3.getTitle());

        // 使用.orElseGet()获取值,如果Optional为空则通过Supplier接口实现来生成返回对象
        Supplier<Book> bookSupplier = () -> new Book(2, "备用图书", "备用作者");
        Book retrievedBook4 = nullableOptional.orElseGet(bookSupplier);
        System.out.println("通过.orElseGet()获取到的图书(可能为空的情况): " + retrievedBook4.getTitle());

        // 使用.orElseThrow()获取值,如果Optional为空则抛出由Supplier接口提供的异常
        try {
            Supplier<Exception> exceptionSupplier = () -> new RuntimeException("图书不存在异常");
            Book retrievedBook5 = nullableOptional.orElseThrow(exceptionSupplier);
        } catch (Exception e) {
            System.out.println("通过.orElseThrow()抛出异常(可能为空的情况): " + e.getMessage());
        }

        // 4. 处理操作示例

        // 过滤操作示例,假设只保留作者是特定作者的图书对应的Optional对象
        Optional<Book> filteredOptional = nonNullOptional.filter(book -> "Cay Horstmann".equals(book.getAuthor()));
        System.out.println("过滤后的Optional是否存在图书: " + filteredOptional.isPresent());

        // 普通转换操作示例,将图书Optional对象转换为书名的Optional对象
        Optional<String> titleOptional = nonNullOptional.map(Book::getTitle);
        System.out.println("通过.map()转换后的Optional书名: " + titleOptional.orElse("无书名"));

        // 一转多操作示例,假设每本图书有多个分类,用字符串数组表示,将图书Optional对象转换为分类字符串流的Optional对象(这里简单模拟下)
        Optional<Book> anotherBookOptional = Optional.of(new Book(3, "Python从入门到实践", "Eric Matthes"));
        Optional<String[]> categoriesOptional = anotherBookOptional.flatMap(book -> {
            String[] categories = {"编程", "Python"};
            return Optional.of(categories);
        });
        if (categoriesOptional.isPresent()) {
            for (String category : categoriesOptional.get()) {
                System.out.println("通过.flatMap()转换后的图书分类: " + category);
            }
        }

        // 注意事项验证示例

        // 验证filter会先判断是否为null再执行传入的函数
        nullableOptional.filter(book -> "不存在的作者".equals(book.getAuthor()));
        System.out.println("经过filter(null情况)后Optional是否为空: " + nullableOptional.isPresent());

        // 验证map会先判断是否为null再执行传入的函数
        nullableOptional.map(book -> book.getTitle());
        System.out.println("经过map(null情况)后Optional是否为空: " + nullableOptional.isPresent());

        // 验证flatMap会先判断是否为null再执行传入的函数
        nullableOptional.flatMap(book -> {
            String[] fakeCategories = {"无效分类"};
            return Optional.of(fakeCategories);
        });
        System.out.println("经过flatMap(null情况)后Optional是否为空: " + nullableOptional.isPresent());
    }
}

结果:

存在图书: Java核心技术
通过.get()获取到的图书: Java核心技术
获取空的Optional对象的值时出错: No value present
通过.orElse()获取到的图书(可能为空的情况): 默认图书
通过.orElseGet()获取到的图书(可能为空的情况): 备用图书
通过.orElseThrow()抛出异常(可能为空的情况): 图书不存在异常
过滤后的Optional是否存在图书: true
通过.map()转换后的Optional书名: Java核心技术
通过.flatMap()转换后的图书分类: 编程
通过.flatMap()转换后的图书分类: Python
经过filter(null情况)后Optional是否为空: false
经过map(null情况)后Optional是否为空: false
经过flatMap(null情况)后Optional是否为空: false

方法引用

用法:

类名::方法名

使用前提:

​ 重写方法时只有一行代码且是一个方法调用。

​ 不用特别记忆,写完lambda,可以alt + enter让编译器来试试。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cosmoshhhyyy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值