Stream
- 流是即时计算的,按需提供,要什么生产什么。集合得先生产好所有的再使用。
集合里的元素必须是计算出来的,真实存在内存中。流则是在概念上固定的数据结构(你不能添加或删除元素),其元素则是按需计算的,要什么就算什么。 - 流只能遍历一次,遍历完就被消费了,无法第二次使用,只能重新再获取一次。
- 外部迭代是一个一个判断,去遍历,流的内部迭代类似直接下命令:全部进行XXX操作。内部迭代时,项目可以透明地并行处理,或者用更优化的顺 序进行处理
- Stream接口定义了许多操作
- 中间操作
- 如filter或sorted等中间操作会返回另一个流,这让多个操作可以连接起来形成一个查询。除非流水线上触发一个终端操作,否则中间操作不会执行任何处理。中间操作一般都可以合并起来,在终端操作时一次性全部处理。
- 终端操作
- 终端操作会从流的流水线生成结果。其结果为任何不是流的值,比如List、Integer,甚至void
- 循环合并
- 如filter和map是两个独立的操作,但是可以合并到一次遍历中。看起来是这样
- 中间操作
filtering pork
mapping pork
filtering beef
mapping beef
filtering chicken
mapping chicken
- 接口
- filter() 筛选
- map() 映射、获取值
- limit() 取前几项
- sorted() 排序
- Arrays.stream() 接受数组并把数组转换为类型流(如把String数组转化为String流)
- distinct() 去除重复元素
- 扁平化
- flatmap方法把一个流中的每个值都换成另一个流,然后把所有的流连接起来成为一个流。
- map方法
- 查询和匹配
- allMatch 所有元素都和谓语匹配
- antMatch 至少一个元素都不和谓语匹配
- noneMatch 所有元素都不和谓语匹配
- 归约(将流中所有元素反复结合起来,最终得到一个值)
- reduce
- (1, (a, b) -> a * b) 所有元素相乘
- (0, (a, b) -> a + b) 所有元素相加
- reduce(Integer::max)
- reduce(Integer::min) 等于(x, y) -> x < y ? x : y
- count() 统计流中的元素的个数
- reduce
- 原始类型流特化(避免暗含的装箱)
- IntStream、DoubleStream和 LongStream,分别将流中的元素特化为int、long和double
- 将流转换为特化版本的常用方法是mapToInt、mapToDouble和mapToLong。
- 转回非特化流
- Stream stream = intStream.boxed();
- 数值范围
- range(起始数值, 结束数值) 不包括结束数值,(1, 5)只取一到4
- rangeClosed(起始数值, 结束数值) 包括结束数值,(1, 5)取一到5
- 预定义收集器(如groupingBy)
- 将元素归约汇总为一个值、元素分组、元素分区
- 汇总 .collect(summingInt(Dish::getCalories))
- 平均数 .collect(averagingInt(Dish::getCalories));
- summarizing操作,一次就能收集总数、min、max、平均数
- .collect(summarizingInt(Dish::getCalories)) 这个收集器会把所有这些信息收集到一个叫作IntSummaryStatistics的类里,它提供了 方便的取值(getter)方法来访问结果。
菜单练习中打印IntSummaryStatistics的结果
- .collect(summarizingInt(Dish::getCalories)) 这个收集器会把所有这些信息收集到一个叫作IntSummaryStatistics的类里,它提供了 方便的取值(getter)方法来访问结果。
IntSummaryStatistics{count=9, sum=4300, min=120, average=477.777778, max=800}
- 连接字符串
- joining工厂方法返回的收集器会把对流中每一个对象应用toString方法得到的所有字符 串连接成一个字符串
- joining在内部使用了StringBuilder来把生成的字符串逐个追加起来
- .collect(joining(", ")); 重载方法,使用分隔符增加可读性
menu.stream().map(Dish::getName).collect(joining());
menu.stream().collect(joining()); // 类中有toString方法就不需要手动提取。
menu.stream().collect(joining(", ")); //加入分隔符
- 自定义收集器(定义自己的Collector接口)
public interface Collector<T, A, R>{
Supplier supplier();
BiConsumer accumulator();
Function finisher();
BinaryOperator combiner();
Set characteristics();
}
- T是流中要收集的元素的类型
- A是累加器的类型,累加器是在收集过程中用于累加结果的对象
- R是收集得到的最终结果的类型
- supplier方法:返回一个空的累加器作为收集过程的容器
public Supplier<List<T>> supplier() {
return ArrayList::new;
}
- accumulator方法 :将元素添加到结果容器
public BiConsumer<List<T>, T> accumulator() {
return (list, item) -> list.add(item);
}
public BiConsumer, T> accumulator() {
return List::add;
}
- finisher方法:对收集容器进行最终的转换,成为结果容器
public Function<List<T>> finisher(){
return Function.identity();
}
// 当累加器对象与结果对象符合时,无需进行转换操作,直接调用identity函数。
- combiner方法:将拆分出来的并行流进行归约操作,最终合并到一起。
public BinaryOperator<List<T>> combiner() {
return (list1, list2) -> {list1.addAll(list2); return list1;}
}
- characteristics方法
- characteristics会返回一个不可变的Characteristics集合,它定义 了收集器的行为——尤其是关于流是否可以并行归约,以及可以使用哪些优化的提示。 Characteristics是一个包含三个项目的枚举
- UNORDERED——归约结果不受流中项目的遍历和累积顺序的影响。
- CONCURRENT——accumulator函数可以从多个线程同时调用,且该收集器可以并行归 约流。如果收集器没有标为UNORDERED,那它仅在用于无序数据源时才可以并行归约。
- IDENTITY_FINISH——这表明完成器方法返回的函数是一个恒等函数,可以跳过。这种 情况下,累加器对象将会直接用作归约过程的最终结果。这也意味着,将累加器A不加检 查地转换为结果R是安全的。
- characteristics会返回一个不可变的Characteristics集合,它定义 了收集器的行为——尤其是关于流是否可以并行归约,以及可以使用哪些优化的提示。 Characteristics是一个包含三个项目的枚举
- collect方法的重载
- Stream有一个重载的collect方法可以接受另外三个函数——supplier、 accumulator和combiner,其语义和Collector接口的相应方法返回的函数完全相同。不能传递任何Characteristics,所以它永远都是一个IDENTITY_FINISH和 CONCURRENT但并非UNORDERED的收集器。
例:
List<DIsh> dishes = menuStream.collect(
ArrayList::new,
List::add,
List::addAll);
未完待续...
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本