stream概要与使用场景
stream的概要
stream与InputStream和outputStream不同,是用于对集合迭代器的增强,使之能够完成更高效率的聚合操作(过滤,排序,统计分组),或者大批量的数据操作. 此外与stream与lambada表达式节后后编码效率大大提高,并且可读性更强
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
再学习stream之前,需要对Lambda表达式有一定的了解。
下面这个例子可以解释Stream可以做什么:
上面的例子中,获取一些带颜色塑料球作为数据源,首先过滤掉红色的、把它们融化成随机的三角形。再过滤器并删除小的三角形。最后计算出剩余图形的周长。
如上图,对于流的处理,主要有三种关键性操作:分别是流的创建、中间操作(intermediate operation)以及最终操作(terminal operation)。
用更简洁的话来说,就是将集合中的元素放在流水线上,并在每一步添加条件,筛选部分元素,最后留下的就是你想要的元素。
stream的操作流程
对于流的处理,主要有三种关键性操作:分别是流的创建、中间操作(intermediate operation)以及最终操作(terminal operation)。
- 流创建:就是stream()方法
- 中间操作:筛选、排序这类操作
- 最终操作:取出、打印这些操作
下面会对这三类操作做具体的介绍
管道源/流创建
将Collection切换为管道源很简单,调用stream
heros.stream();
但是数组没有stream方法,需要使用
Arrays.stream(hs);
或者
Stream.of(hs);
这里不要和I/O操作中的InputStream、OutputStream搞混了,两个不是同一个概念。
中间操作
每个中间操作,又会返回一个Stream,比如.filter()又返回一个Stream, 中间操作是“懒”操作,并不会真正进行遍历。
中间操作比较多,主要分两类
对元素进行筛选 和 转换为其他形式的流
对元素进行筛选:
filter 匹配
distinct 去除重复(根据equals判断) //注意:Object里的equals判断的是不是同一个对象,下面是Object中equals()的源码:public boolean equals(Object obj) { return this == obj; }
但是String中的equals判断的是字符串内容是否一样(当然如果是同一个字符串对象的话,返回的也是true)
sorted 自然排序
sorted(Comparator) 指定排序
limit 保留
skip 忽略
转换为其他形式的流:
mapToDouble 转换为double的流
类似的还有mapToInt(),mapToLong()map 转换为任意类型的流
package lambda;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import charactor.Hero;
public class TestAggregate {
public static void main(String[] args) {
Random r = new Random();
List<Hero> heros = new ArrayList<Hero>();
for (int i = 0; i < 5; i++) {
heros.add(new Hero("hero " + i, r.nextInt(1000), r.nextInt(100)));
}
//制造一个重复数据
heros.add(heros.get(0));
System.out.println("初始化集合后的数据 (最后一个数据重复):");
System.out.println(heros);
System.out.println("满足条件hp>100&&damage<50的数据");
heros
.stream()
.filter(h->h.hp>100&&h.damage<50)
.forEach(h->System.out.print(h));
System.out.println("去除重复的数据,去除标准是看equals");
heros
.stream()
.distinct()
.forEach(h->System.out.print(h));
System.out.println("按照血量排序");
heros
.stream()
.sorted((h1,h2)->h1.hp>=h2.hp?1:-1)
.forEach(h->System.out.print(h));
System.out.println("保留3个");
heros
.stream()
.limit(3)
.forEach(h->System.out.print(h));
System.out.println("忽略前3个");
heros
.stream()
.skip(3)
.forEach(h->System.out.print(h));
System.out.println("转换为double的Stream");
heros
.stream()
//这里虽然获取的是血量
//但是操作的还是对象
.mapToDouble(Hero::getHp)
.forEach(h->System.out.println(h));
System.out.println("转换任意类型的Stream");
heros
.stream()
.map((h)-> h.name + " - " + h.hp + " - " + h.damage)
.forEach(h->System.out.println(h));
}
}
再看一个实例
List<String> strings = Arrays.asList("Hollis", "HollisChuang", "hollis", "Hello", "HelloWorld", "Hollis");
Stream s = strings.stream()
.filter(string -> string.length()<= 6)
.map(String::length)
.sorted()
.limit(3)
.distinct();
下图是Stream对象的变化过程
最终操作
Stream的中间操作得到的结果还是一个Stream,那么如何把一个Stream转换成我们需要的类型呢?比如计算出流中元素的个数、将流装换成集合等。这就需要最终操作(terminal operation)
最终操作会消耗流,产生一个最终结果。也就是说,在最终操作之后,不能再次使用流,也不能在使用任何中间操作,否则将抛出异常。
常见的结束操作如下:
forEach() 遍历每个元素
collect()
toArray() 转换为数组
min(Comparator) 取最小的元素
max(Comparator) 取最大的元素
count() 用来统计流中的元素个数
findFirst() 第一个元素
下面是一个使用collect的例子
List<String> strings = Arrays.asList("Hollis", "HollisChuang", "hollis","Hollis666", "Hello", "HelloWorld", "Hollis");
strings = strings
.stream()
.filter(string -> string.startsWith("Hollis"))
.collect(Collectors.toList());
//找出以“Hollis”开头的元素
System.out.println(strings);
//Hollis, HollisChuang, Hollis666, Hollis
总结
Java中的Stream就像数据库的查询功能,能够帮助我们写出更加高效的代码