所有数据都基于UserInfo类,其中包含了 userId、userName、course、score 等字段,下面是如何使用Options、 Stream 、Function来处理 UserInfo 对象列表的一些示例
List<UserInfo> userInfoList = Arrays.asList(new UserInfo(1L, "Alice", "Math", 90),new UserInfo(2L, "Bob", "Physics", 85),new UserInfo(3L, "Charlie", "Chemistry", 88),new UserInfo(4L, "Diana", "Math", 92),new UserInfo(5L, "Eve", "Physics", 89));@Data
public static class UserInfo {private Long userId;private String userName;private String course;private int score;public UserInfo(Long userId, String userName, String course, int score) {this.userId = userId;this.userName = userName;this.course = course;this.score = score;}public String getCourse() {return course;}
}
所有数据都基于UserInfo类,其中包含了 userId、userName、course、score 等字段,下面是如何使用 Java Stream API 来处理 UserInfo 对象列表的一些示例
List<UserInfo> userInfoList = List.of(new UserInfo(1L, "Alice", "Math", 90),new UserInfo(2L, "Bob", "Physics", 85),new UserInfo(3L, "Charlie", "Chemistry", 88),new UserInfo(4L, "Diana", "Math", 92),new UserInfo(5L, "Eve", "Physics", 89));
一、Stream用法
Stream是Java 8中引入的全新API,可以极大地方便我们对集合、数组等数据源进行连续操作
1.1 流的创建
通过集合 Collection.stream() 创建 Stream,一般开发中常用这种方式创建流
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Stream<String> streamFromCollection = names.stream();
通过数组 Arrays.stream 创建 Stream
String[] names = {"Alice", "Bob", "Charlie"};
Stream<String> streamFromArray = Arrays.stream(names);
通过 Stream.of() 方法创建 Stream
Stream<String> streamFromOf = Stream.of("Alice", "Bob", "Charlie");
使用 Stream.iterate() 创建无限流
Stream<Long> infiniteStream = Stream.iterate(0L, n -> n + 2).limit(5); // 限制元素数量
⚠️注意:Stream.iterate() 方法可以用来创建无限流,但在实际使用时通常会配合limit()方法来限制流中的元素数量,以避免无限循环
1.2 流的使用
1.2.1 foreach遍历
🌰示例 : 打印所有用户的信息
userInfoList.stream().forEach(System.out::println);
1.2.2 filter过滤
🌰示例:找出所有学数学的学生
List<UserInfo> userList = userInfoList.stream().filter(user -> "Math".equals(user.getSubject())).collect(Collectors.toList());
1.2.3 sorted排序
sorted支持两种方式:
//自然排序,元素须实现 Comparable 接口,并且 compareTo 方法将被用于确定排序顺序
Stream<T> sorted();
//自定义排序,使用 Comparator
Stream<T> sorted(Comparator<? super T> var1);
🌰示例:按照分数升序排序
List<UserInfo> userList = userInfoList.stream().sorted(Comparator.comparing(UserInfo::getScore)).collect(Collectors.toList());
1.2.5 map/flatMap映射
🌰示例 : 获取所有用户的名字userName
List<String> userNameList = userInfoList.stream().map(UserInfo::getUserName).collect(Collectors.toList());
1.2.4 distinct 去重
🌰示例 :获取所有学科名称
List<String> courseList = userInfoList.stream().map(UserInfo::getCourse).distinct().collect(Collectors.toList());
1.2.6 reduce规约
🌰示例 : 计算总分
int totalScore = userInfoList.stream().map(UserInfo::getScore).reduce(Integer::sum).orElse(0); //流中没有任何元素时返回默认值0//或者默认初始值为0 //.reduce(0, Integer::sum)
1.2.7 collect收集
1.2.7.1 toList/toSet/toMap归集
toList/toSet
🌰示例 :将用户收集到新的集合中
List<UserInfo> userList = userInfoList.stream().collect(Collectors.toList());Set<UserInfo> userList = userInfoList.stream().collect(Collectors.toSet());
toMap有一点特殊
🌰示例2:获取userInfoList的映射集合Map<userId,UserInfo>
Map<String, UserInfo> userInfoMap = userInfoList.stream().collect(Collectors.toMap(UserInfo::getUserId, userInfo -> userInfo));
Map<String, UserInfo> userInfoMap = userInfoList.stream().collect(Collectors.toMap(UserInfo::getUserId, Function.identity()));
lambda 表达式 userInfo -> userInfo 作为第二个参数传递给 Collectors.toMap()。这与使用 Function.identity() 的效果相同,因为 Function.identity() 本质上就是返回一个函数,该函数接收一个参数并返回相同的参数
⚠️:可能会出现Duplicate Key
因此:
Map<Long, UserInfo> userInfoMap = userInfoList.stream().collect(Collectors.toMap(UserInfo::getUserId,Function.identity(), (newVal,oldVal)->newVal));
🌰示例:按照userInfoList的插入顺序转为集合Map<userId,UserInfo>
Map<Long, UserInfo> userInfoMap = userInfoList.stream().collect(Collectors.toMap(UserInfo::getUserId,Function.identity(), (newVal,oldVal)->newVal),LinkedHashMap::new
);
1.2.7.2 groupingBy分组
🌰示例:将所有用户按课程分组
Map<String, List<UserInfo>> usersByCourse = userInfoList.stream().collect(Collectors.groupingBy(UserInfo::getCourse));
1.2.7.3 joining拼接
🌰示例:将所有的用户名使用“,”拼接成字符串返回
String allName = userInfoList.stream().map(UserInfo::getName).collect(Collectors.joining(", "));
1.2.7.4 counting/summingInt/averagingInt/summarizingInt聚合
计数:counting
最值:maxBy、minBy
求和:summingInt、summingLong、summingDouble
平均值:averagingInt、averagingLong、averagingDouble
统计以上所有:summarizingInt、summarizingLong、summarizingDouble,其中包含了流中元素的计数、总和、平均值、最小值和最大值
🌰示例 : 计算总分
使用 .collect 和 Collectors.summingInt
int totalScore = userInfoList.stream().collect(Collectors.summingInt(UserInfo::getScore));
1.2.7.4 reducing 规约
🌰示例 : 计算总分
Integer toatlScore = userInfoList.stream().collect(Collectors.reducing(0, UserInfo::getScore, Integer::sum));
1.2.7.5 max/min/sum/count聚合
🌰示例 :获取最高分
int maxScore = userInfoList.stream().mapToInt(UserInfo::getScore).max().orElse(0);
⚠️:此处如果使用.map(UserInfo::getScore) 会错误,由于于Stream和IntStream的区别,以及它们各自提供的max()方法的不同
Stream的max()需要实现Comparator来比较元素
Optional<T> max(Comparator<? super T> comparator)
而IntStream的max()
OptionalInt max();
🌰示例 : 计算总分
和reduce求和的结果是一样,但sum方法通常会更快一些,因为它直接针对IntStream进行了优化
int totalScore = userInfoList.stream().mapToInt(UserInfo::getScore).sum()
1.2.7.6 peek查看
1.2.7 limit/skip
🌰示例:按分数排序
serName
List<String> userNames = userInfoList.stream().map(UserInfo::getUserName).collect(Collectors.toList());
1.2
🌰示例:将所有用户按课程course分组
Map<String, List<UserInfo>> usersByCourse = userInfoList.stream().collect(Collectors.groupingBy(UserInfo::getCourse));
🌰示例:将所有用户的分数求和
📌方法1:使用 .map 和 .reduce
int totalScore = userInfoList.stream().map(UserInfo::getScore).reduce(0, Integer::sum);
📌方法2:使用 .collect 和 Collectors.summingInt
int totalScore = userInfoList.stream().collect(Collectors.summingInt(UserInfo::getScore));
📌方法3:使用 .mapToInt 和 .sum
int totalScore = userInfoList.stream() .mapToInt(UserInfo::getScore) .sum();
Lambda使用了函数式编程,在jdk1.8中内置了四个常用的函数式编程的接口,分别是:Function、Predicate、Supplier、Consumer。下面我们来分别介绍下这些类
java -Function、Predicate、Supplier、Consumer及自定义实现
二、Optional
不使用Optional的示例:需要手动检查列表是否为空,并处理空值情况。
使用Optional的示例:简化了代码,通过Optional自动处理空值情况
1. 创建Optional对象
Optional.of(T value):创建一个包含非空值的Optional对象。
Optional.ofNullable(T value):创建一个包含非空值的Optional对象,如果值为null则返回一个空的Optional对象
Map<Long, Map<String, Long>> meterTypeMap = Optional.ofNullable(meterTypeList)
.orElse(Collections.emptyList()).stream()
.collect(Collectors.groupingBy(DeviceMeterTypeEntity::getProjectId, Collectors.toMap(DeviceMeterTypeEntity::getMeterSign, DeviceMeterTypeEntity::getDeviceTypeId)));
通过这些示例,我们可以看到Optional如何帮助我们安全地处理可能为空的情况,并提供默认值。这样可以避免空指针异常,使代码更加健壮。Optional的主要用法包括:
创建Optional对象:使用Optional.of或Optional.ofNullable。
检查Optional是否为空:使用isPresent或isEmpty。
获取Optional中的值:使用get或orElse。
处理Optional中的值:使用ifPresent或ifPresentOrElse。
转换Optional中的值:使用map或flatMap。
二、Function用法
在Java中,Function<T,R>接口是Java 8引入的函数式接口之一,它代表了一个将一个类型的参数映射到另一个类型的返回值的函数。下面通过UserInfo类的例子来展示Function的几种常见用法:
Function函数有三个主要的方法:apply、compose、andThen
apply:该方法意义是执行当前函数的方法体。
compose(before):先执行before函数方法体,使用其返回参数,再执行调用者函数的方法体。
andThen (after):先执行调用者方法,将其返回值作为参数调用after函数方法体
使用Function
Function<UserInfo, Long> getUserId = UserInfo::getUserId;