算我求你了!请避开Java Stream流式编程常见的坑
Java8 由Oracle在2014年发布 , 是继Java5之后最具革命性的版本了 。
Java8吸收其他语言的精髓带来了函数式编程 , lambda表达式 , Stream流等一系列新特性 , 学会了这些新特性 , 可以让你实现高效编码优雅编码 。
1. Stream是什么?Stream是Java8新增的一个接口 , 允许以声明性方式处理数据集合 。 Stream不是一个集合类型不保存数据 , 可以把它看作是遍历数据集合的高级迭代器(Iterator) 。
Stream操作可以像Builder一样逐步叠加 , 形成一条流水线 。 流水线一般由数据源+零或者多个中间操作+一个终端操作所构成 。 中间操作可以将流转换成另外一个流 , 比如使用filter过滤元素 , 使用map映射提取值 。
Stream与lambda表达式密不可分 , 本文默认你已经掌握了lambda基础知识 。
2. Stream的特点
- 只能遍历(消费)一次 。 Stream实例只能遍历一次 , 终端操作后一次遍历就结束 , 再次遍历需要重新生成实例 , 这一点类似于Iterator迭代器 。
- 保护数据源 。 对Stream中任何元素的修改都不会导致数据源被修改 , 比如过滤删除流中的一个元素 , 再次遍历该数据源依然可以获取该元素 。
- 懒 。 filter, map 操作串联起来形成一系列中间运算 , 如果没有一个终端操作(如collect)这些中间运算永远也不会被执行 。
// of为Stream的静态方法Stream strStream = Stream.of("hello", "java8", "stream");// 或者使用基本类型流IntStream intStream = IntStream.of(1, 2, 3);复制代码
(2)使用集合创建Stream实例(常用方式)// 使用guava库 , 初始化一个不可变的list对象ImmutableList integers = ImmutableList.of(1, 2, 3);// List接口继承Collection接口 , java8在Collection接口中添加了stream方法Stream stream = integers.stream();复制代码
(3)使用数组创建Stream实例// 初始化一个数组Integer[] array = {1, 2, 3};// 使用Arrays的静态方法streamStream stream = Arrays.stream(array);复制代码
(4)使用生成器创建Stream实例// 随机生成100个整数Random random = new Random();// 加上limit否则就是无限流了Stream stream = Stream.generate(random::nextInt).limit(100);复制代码
(5)使用迭代器创建Stream实例// 生成100个奇数 , 加上limit否则就是无限流了Stream stream = Stream.iterate(1, n -> n + 2).limit(100);stream.forEach(System.out::println);复制代码
(6)使用IO接口创建Stream实例// 获取指定路径下文件信息 , list方法返回Stream类型Stream pathStream = Files.list(Paths.get("/"));复制代码
4. Stream常用操作Stream接口中定义了很多操作 , 大致可以分为两大类 , 一类是中间操作 , 另一类是终端操作;文章插图
(1)中间操作
中间操作会返回另外一个流 , 多个中间操作可以连接起来形成一个查询 。
中间操作有惰性 , 如果流上没有一个终端操作 , 那么中间操作是不会做任何处理的 。
下面介绍常用的中间操作:
map操作
map是将输入流中每一个元素映射为另一个元素形成输出流 。
// 初始化一个不可变字符串List words = ImmutableList.of("hello", "java8", "stream");// 计算列表中每个单词的长度List list = words.stream().map(String::length).collect(Collectors.toList());// output: 5 5 6list.forEach(System.out::println);复制代码
flatMap操作List list1 = words.stream().map(word -> word.split("-")).collect(Collectors.toList());// output: [Ljava.lang.String;@59f95c5d, //[Ljava.lang.String;@5ccd43c2list1.forEach(System.out::println);复制代码
哪里?你预期是List, 返回却是List, 这是因为split方法返回的是String[]这个时候你可以想到要将数组转成stream, 于是有了第二个版本
Stream> arrStream = words.stream().map(word -> word.split("-")).map(Arrays::stream);// output: java.util.stream.ReferencePipeline$Head@2c13da15, // java.util.stream.ReferencePipeline$Head@77556fdarrStream.forEach(System.out::println);复制代码
还是不对 , 这个问题使用flatMap扁平流可以解决 , flatMap将流中每个元素取出来转成另外一个输出流Stream strStream = words.stream().map(word -> word.split("-")).flatMap(Arrays::stream).distinct();// output: hello java8 streamstrStream.forEach(System.out::println);复制代码
filter操作filter接收Predicate对象 , 按条件过滤 , 符合条件的元素生成另外一个流 。
// 过滤出单词长度大于5的单词 , 并打印出来List words = ImmutableList.of("hello", "java8", "hello", "stream");words.stream().filter(word -> word.length() > 5).collect(Collectors.toList()).forEach(System.out::println);// output: stream复制代码
- 发展|我省要求互联网平台坚持依法合规经营 推动线上经济健康规范发展
- 现状|程序员现状揭秘:平均年薪20.36万,Java人才需求量最大
- 印度|拒绝华为后,印度、英国斥资数十亿求助日本
- 承受|折叠屏iPhone已开始测试?要求能承受10万次折叠,或在2年后发布
- 区企联企协|谋求更高质量的转型发展!区企联企协与区科技局成功举办科技考察对接活动
- 品牌|为求差异化 山姆升级自有品牌Member’s Mark
- 中国|意大利制造求助中国网站,意外交部长出马见证
- 需求|需求下降!传三星可能停售高端Galaxy Note智能手机,重心转移至可折叠手机
- 系列|首销300000台!红米Note 9系列,或许可以说恭喜你了?
- 高像素|加持高像素只为解析力?vivo S7丛林秘境展对样张细节的要求更严苛