Stream,就是JDK8又依托于函数式编程特性为集合类库做的一个类库,它其实就是jdk提供的函数式接口的最佳实践。它能让我们通过lambda表达式更简明扼要的以流水线的方式去处理集合内的数据,可以很轻松的完成诸如:过滤、分组、收集、归约这类操作。
Stream的操作大致分为两类
其中转换型操作又分为有状态和无状态两类。有状态是本次的结果需要依赖于前面的处理结果,而无状态则是不依赖。简单来讲就是无状态方法可以互相调换位置,而有状态方法不能调换位置。
中间型操作就是返回值依旧是stream类型的方法。api如下:
API | 功能说明 | 无状态操作 |
filter() | 按照条件过滤符合要求的元素, 返回新的stream流。 | 是 |
map() | 将已有元素转换为另一个对象类型,一对一逻辑,返回新的stream流 | 是 |
peek() | 对stream流中的每个元素进行逐个遍历处理,返回处理后的stream流 | 是 |
flatMap() | 将已有元素转换为另一个对象类型,一对多逻辑,即原来一个元素对象可能会转换为1个或者多个新类型的元素,返回新的stream流 | 是 |
limit() | 仅保留集合前面指定个数的元素,返回新的stream流 | 否 |
skip() | 跳过集合前面指定个数的元素,返回新的stream流 | 否 |
concat() | 将两个流的数据合并起来为1个新的流,返回新的stream流 | 否 |
distinct() | 对Stream中所有元素进行去重,返回新的stream流 | 否 |
sorted() | 对stream中所有的元素按照指定规则进行排序,返回新的stream流 | 否 |
takeWhile() | JDK9新增,传入一个断言参数当第一次断言为false时停止,返回前面断言为true的元素。 | 否 |
dropWhile() | JDK9新增,传入一个断言参数当第一次断言为false时停止,删除前面断言为true的元素。 | 否 |
终结型操作与中间型相反,返回值是非Stream类型的。api如下:
API | 功能说明 |
count() | 返回stream处理后最终的元素个数 |
max() | 返回stream处理后的元素最大值 |
min() | 返回stream处理后的元素最小值 |
findFirst() | 找到第一个符合条件的元素时则终止流处理 |
findAny() | 找到任何一个符合条件的元素时则退出流处理,这个对于串行流时与findFirst相同,对于并行流时比较高效,任何分片中找到都会终止后续计算逻辑 |
anyMatch() | 返回一个boolean值,类似于isContains(),用于判断是否有符合条件的元素 |
allMatch() | 返回一个boolean值,用于判断是否所有元素都符合条件 |
noneMatch() | 返回一个boolean值, 用于判断是否所有元素都不符合条件 |
*collect() | 将流转换为指定的类型,通过Collectors进行指定 |
*reduce() | 将一个Stream中的所有元素反复结合起来,得到一个结果 |
toArray() | 将流转换为数组 |
iterator() | 将流转换为Iterator对象 |
foreach() | 无返回值,对元素进行逐个遍历,然后执行给定的处理逻辑 |
基本语法: (parameters) -> expression 或 (parameters) ->{ statements; }
Lambda表达式由三部分组成:
Lambda表达式的优点很明显,在代码层次上来说,使代码变得非常的简洁。缺点也很明显,代码不易读。
新建实体对象SysUser 并初始化。
/**
* @author hope
* @date 2023年08月22日 16:10
*/
@Data
@Accessors(chain = true)
public class SysUser {
private Long id;
private String name;
private Integer age;
private BigDecimal amount = BigDecimal.ZERO;
public SysUser(Long id, String name, Integer age, BigDecimal amount) {
this.id = id;
this.name = name;
this.age = age;
this.amount = amount;
}
}
List userList = new ArrayList<>();
userList.add(new SysUser(10000L, "zhangsan", 18, new BigDecimal("200")));
userList.add(new SysUser(20000L, "lisi", 27, new BigDecimal("180")));
userList.add(new SysUser(30000L, "wangwu", 26, new BigDecimal("210")));
/**
* list 转 Map
* 使用Collectors.toMap的时候,如果有可以重复会报错,所以需要加(k1, k2) -> k1
* (k1, k2) -> k1 表示,如果有重复的key,则保留第一个,舍弃第二个
*/
Map userMap2 = userList.stream().collect(Collectors.toMap(SysUser::getId, userInfo -> userInfo, (k1, k2) -> k1));
从数组集合中,过滤掉不符合条件的元素,留下符合条件的元素。
/**
* filter 过滤,查询出超过18岁且金额大于100的用户
*/
List userResultList = userList.stream().filter(user -> user.getAge() > 18 && user.getAmount().compareTo(new BigDecimal("100"))==1).collect(Collectors.toList());
foreach 遍历list,遍历map。
/**
* 遍历
*/
userList.stream().forEach(sysUser -> System.out.println(sysUser.getName()));
使用Java8的groupingBy
分组器:
/**
* 分组
*/
Map> userMap = userList.stream().collect(Collectors.groupingBy(SysUser::getId));
/**
* sorted + Comparator.comparing 排序列表,
*/
userList = userList.stream().sorted(Comparator.comparing(SysUser::getAge)).collect(Collectors.toList());
/**
* 降序排序,则可以使用加reversed()
*/
userList = userList.stream().sorted(Comparator.comparing(SysUser::getAge).reversed()).collect(Collectors.toList());
distinct
可以去除重复的元素
/**
* distinct去重
*/
userList = userList.stream().distinct().collect(Collectors.toList());
/**
* 获取第一个对象
*/
Optional optional = userList.stream().findFirst();
SysUser user = optional.get();
anyMatch
检查流是否包含至少一个满足给定谓词的元素。
/**
* 是否匹配
*/
Boolean b = userList.stream().anyMatch(user->user.getAge()>18);
allMatch
检查流是否所有都满足给定谓词的元素。
/**
* 匹配所有元素
*/
Boolean b = userList.stream().allMatch(user->user.getAge()>18);
/**
* map转换
*/
List ret = userList.stream().map(SysUser::getAmount).collect(Collectors.toList());
Reduce可以合并流的元素,并生成一个值
/**
* reduce求和
*/
BigDecimal money = userList.stream().map(SysUser::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
Optional num = userList.stream().map(SysUser::getAge).reduce(Integer::sum);
int sum = Stream.of(10, 20, 30, 40).reduce(0, (a, b) -> a + b);
peek()
方法是一个中间Stream
操作,使用peek
来打印日志。
/**
* peek打印日志
*/
userList = userList.stream().filter(u -> u.getAge() > 18).peek(p-> System.out.println("年轻人:" + p.getName())).collect(Collectors.toList());
13. Max,Min最大最小使用lambda流求最大,最小值。
/**
* Max-最大值
*/
Optional optional = userList.stream().max(Comparator.comparingInt(SysUser::getAge));
System.out.println(optional.get().getAge()); // 27
/**
* Min-最小值
*/
Optional optional2 = userList.stream().min(Comparator.comparingInt(SysUser::getAge));
System.out.println(optional2.get().getAge()); // 18
count()
获取流数据元素总数。
/**
* count统计
*/
long count = userList.stream().filter(u -> u.getAge() > 18).count();
Function
(转换型): 接受一个输入参数,返回一个结果
Consumer
(消费型): 接收一个输入参数,并且无返回操作
Predicate
(判断型): 接收一个输入参数,并且返回布尔值结果
Supplier
(供给型): 无参数,返回结果
Function
是一个功能转换型的接口,可以把将一种类型的数据转化为另外一种类型的数据:
/**
* 获取每个字符串的长度,并且返回
*/
Function function = String::length;
Stream stream = Stream.of("zhangsan", "lisi", "wangwu");
Stream resultStream = stream.map(function);
resultStream.forEach(System.out::println);
Consumer
是一个消费性接口,通过传入参数,并且无返回的操作:
/**
* 通过传入参数,并且无返回的操作
*/
Consumer comsumer = System.out::println;
Stream stream = Stream.of("zhangsan", "lisi", "wangwu");
stream.forEach(comsumer);
Predicate
是一个判断型接口,并且返回布尔值结果:
/**
* 判断并且返回布尔值结果
*/
Predicate predicate = a -> a > 18;
SysUser userInfo = new SysUser(40000L, "wangwu", 25, new BigDecimal("150"));
System.out.println(predicate.test(userInfo.getAge())); // true
Supplier
是一个供给型接口,无参数,有返回结果。
Supplier supplier = () -> Integer.valueOf("888888");
System.out.println(supplier.get());
Java 8最大的特性就是引入Lambda表达式,即函数式编程。日常开发中,我们很多时候需要用到Java 8的Lambda表达式,它允许把函数作为一个方法的参数,让我们的代码更优雅、更简洁。