本文章不多说明理论,主要是汇总。
来学习函数式编程就说明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让编译器来试试。