Java8_stream流的常见⽤法
先看下⼏个最近项⽬中⽤到的stream流的例⼦:
if (CollectionUtils.isNotEmpty(schoolVOList)) {
pushUserListRequest.setSchoolIds(schoolVOList.stream().map(SchoolVO::getSchoolId).List()));
}
//根据⽤户ID分组
List userIds = userList.stream().map(PushUserVO::getUserId).List());
//获取⽤户集合
List mobiles = MobileBatch(userIds);
//根据⽤户id分组
Map<Integer, List> accountCollect = mobiles.stream().upingBy(Accounts::getId));
什么是 Stream?
Stream(流)是⼀个来⾃数据源的元素队列并⽀持聚合操作
元素是特定类型的对象,形成⼀个队列。 Java中的Stream并不会存储元素,⽽是按需计算。
数据源流的来源。可以是集合,数组,I/O channel,产⽣器generator 等。
聚合操作类似SQL语句⼀样的操作,⽐如filter, map, reduce, find, match, sorted等。
和以前的Collection操作不同, Stream操作还有两个基础的特征:
Pipelining: 中间操作都会返回流对象本⾝。这样多个操作可以串联成⼀个管道,如同流式风格(fluent style)。这样做可以对操作进⾏优化,⽐如延迟执⾏(laziness)和短路( short-circuiting)。
内部迭代:以前对集合遍历都是通过Iterator或者For-Each的⽅式, 显式的在集合外部进⾏迭代,这叫做外部迭代。 Stream提供了内部迭代的⽅式,通过访问者模式(Visitor)实现。
⽣成流
在 Java 8 中, 集合接⼝有两个⽅法来⽣成流:
stream() − 为集合创建串⾏流。
parallelStream() − 为集合创建并⾏流。
List strings = Arrays.asList(“abc”, “”, “bc”, “efg”, “abcd”,"", “jkl”);
List filtered = strings.stream().filter(string -> !string.isEmpty()).List());
forEach
Stream 提供了新的⽅法 ‘forEach’ 来迭代流中的每个数据。以下代码⽚段使⽤ forEach 输出了10个随机数:
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
map
map ⽅法⽤于映射每个元素到对应的结果,以下代码⽚段使⽤ map 输出了元素对应的平⽅数:
List numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
// 获取对应的平⽅数
List squaresList = numbers.stream().map( i -> i*i).distinct().List());
filter
filter ⽅法⽤于通过设置的条件过滤出元素。以下代码⽚段使⽤ filter ⽅法过滤出空字符串:
Liststrings = Arrays.asList(“abc”, “”, “bc”, “efg”, “abcd”,"", “jkl”);
// 获取空字符串的数量
int count = strings.stream().filter(string -> string.isEmpty()).count();
limit
limit ⽅法⽤于获取指定数量的流。以下代码⽚段使⽤ limit ⽅法打印出 10 条数据:
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
sorted
sorted ⽅法⽤于对流进⾏排序。以下代码⽚段使⽤ sorted ⽅法对输出的 10 个随机数进⾏排序:
Random random = new Random();
random.ints().limit(10).sorted().forEach(System.out::println);
并⾏(parallel)程序
parallelStream 是流并⾏处理程序的代替⽅法。以下实例我们使⽤ parallelStream 来输出空字符串的数量:
List strings = Arrays.asList(“abc”, “”, “bc”, “efg”, “abcd”,"", “jkl”);
// 获取空字符串的数量
int count = strings.parallelStream().filter(string -> string.isEmpty()).count();
java stream我们可以很容易的在顺序运⾏和并⾏直接切换。
Collectors
Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可⽤于返回列表或字符串:
Liststrings = Arrays.asList(“abc”, “”, “bc”, “efg”, “abcd”,"", “jkl”);
List filtered = strings.stream().filter(string -> !string.isEmpty()).List());
System.out.println(“筛选列表: " + filtered);
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(”, "));
System.out.println("合并字符串: " + mergedString);
统计
另外,⼀些产⽣统计结果的收集器也⾮常有⽤。它们主要⽤于int、double、long等基本类型上,它们可以⽤来产⽣类似如下的统计结果。List numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("列表中最⼤的数 : " + Max());
System.out.println("列表中最⼩的数 : " + Min());
System.out.println("所有数之和 : " + Sum());
System.out.println("平均数 : " + Average());
补充:
1. 流与数值流的转换
流转换为数值流
mapToInt(T -> int) : return IntStream
mapToDouble(T -> double) : return DoubleStream
mapToLong(T -> long) : return LongStream
IntStream intStream = list.stream().mapToInt(Person::getAge);
数值流转换为流
很简单,就⼀个 boxed
Stream stream = intStream.boxed();
2. 数值流⽅法
下⾯这些⽅法作⽤不⽤多说,看名字就知道:
sum()
max()
min()
average() 等…
3. 数值范围
IntStream 与 LongStream 拥有 range 和 rangeClosed ⽅法⽤于数值范围处理
IntStream : rangeClosed(int, int) / range(int, int)
LongStream : rangeClosed(long, long) / range(long, long)
这两个⽅法的区别在于⼀个是闭区间,⼀个是半开半闭区间:
rangeClosed(1, 100) :[1, 100]
range(1, 100) :[1, 100)
我们可以利⽤ IntStream.rangeClosed(1, 100) ⽣成 1 到 100 的数值流
求 1 到 10 的数值总和:
IntStream intStream = IntStream.rangeClosed(1, 10);
int sum = intStream.sum();
效率:
最后再来谈谈效率问题,很多⼈可能听说过有关 Stream 效率低下的问题。其实,对于⼀些简单的操作,⽐如单纯的遍历,查最值等等,Stream 的性能的确会低于传统的循环或者迭代器实现,甚⾄会低很多。
但是对于复杂的操作,⽐如⼀些复杂的对象归约,Stream 的性能是可以和⼿动实现的性能匹敌的,在某些情况下使⽤并⾏流,效率可能还远超⼿动实现。好钢⽤在⼑刃上,在适合的场景下使⽤,才能发挥其最⼤的⽤处。
总结:
总之,Stream 的特性可以归纳为:
不是数据结构
它没有内部存储,它只是⽤操作管道从 source(数据结构、数组、generator function、IO channel)抓取数据。
它也绝不修改⾃⼰所封装的底层数据结构的数据。例如 Stream 的 filter 操作会产⽣⼀个不包含被过滤元素的新 Stream,⽽不是从source 删除那些元素。
所有 Stream 的操作必须以 lambda 表达式为参数
不⽀持索引访问
你可以请求第⼀个元素,但⽆法请求第⼆个,第三个,或最后⼀个。不过请参阅下⼀项。
很容易⽣成数组或者 List
惰性化
很多 Stream 操作是向后延迟的,⼀直到它弄清楚了最后需要多少数据才会开始。
Intermediate 操作永远是惰性化的。
并⾏能⼒
当⼀个 Stream 是并⾏化的,就不需要再写多线程代码,所有对它的操作会⾃动并⾏进⾏的。
可以是⽆限的
集合有固定⼤⼩,Stream 则不必。limit(n) 和 findFirst() 这类的 short-circuiting 操作可以对⽆限的 Stream 进⾏运算并很快完成。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论