一、Java8中的Stream
Java8之所以被广大的Java爱好者所喜爱,是因为它提供了一系列的关于集合的Stream操作。集合API是Java API中最重要的部分。基本上每一个java程序都离不开集合。尽管很重要,但是现有的集合处理在很多方面都无法满足需要。
一个原因是,许多其他的语言或者类库以声明的方式来处理特定的数据模型,比如SQL语言,你可以从表中查询,按条件过滤数据,并且以某种形式将数据分组,而不必需要了解查询是如何实现的——数据库帮你做所有的脏活。这样做的好处是你的代码很简洁。很遗憾,Java没有这种好东西,你需要用控制流程自己实现所有数据查询的底层的细节。
其次是你如何有效地处理包含大量数据的集合。理想情况下,为了加快处理过程,你会利用多核架构。但是并发程序不太好写,而且很容易出错。
Stream API很好的解决了这两个问题。它抽象出一种叫做流的东西让你以声明的方式处理数据,更重要的是,它还实现了多线程:帮你处理底层诸如线程、锁、条件变量、易变变量等等。
说了这么多,到底什么是流?通俗地讲,你可以认为是支持类似数据库操作的“花哨的迭代器”。技术上讲,它是从某个数据源获得的支持聚合操作的元素序列。
下面着重介绍一下正式的定义:
元素序列:针对特定元素类型的有序集合流提供了一个接口。但是流不会存储元素,只会根据要求对其做计算。
数据源:流所用到的数据源来自集合、数组或者I/O。
聚合操作:流支持类似数据库的操作以及函数式语言的基本操作,比如filter、map、reduce、findFirst、allMatch、sorted等待。
此外,流操作还有两种额外的基础属性根据不同的集合区分:
管道连接:许多流操作返回流本身,这种操作可以串联成很长的管道,这种方式更加有利于像延迟加载,短路,循环合并等操作。
内部迭代器:不像集合依赖外部迭代器,流操作在内部帮你实现了迭代器。
流接口在java.util.stream.Stream定义了许多操作,这些可以分为以下两类:像filter、sorted和map一样的可以被连接起来形成一个管道的操作。
像collect、findFirst和allMatch一样的终止管道并返回数据的操作。
可以被连接起来的操作被称为中间操作,它们能被连接起来是因为都返回流。中间操作都“很懒”并且可以被优化。终止一个流管道的操作被叫做结束操作,它们从流管道返回像List,Integer或者甚至是void等非流类型的数据。
下面我们介绍一下流里面的一些方法,完整的方法列表可以在java.util.stream.Stream找到。
Filter:有好几个方法可以用来从流里面过滤出元素:
filter:通过传递一个预期匹配的对象作为参数并返回一个包含所有匹配到的对象的流。
distinct:返回包含唯一元素的流(唯一性取决于元素相等的实现方式)。
limit:返回一个特定上限的流。
skip:返回一个丢弃前n个元素的流。
Matching:匹配是一个判断是否匹配到给定属性的普遍的数据处理模式。
Finding:提供了像findFirst和findAny等从流中取出任意的元素。它们能与像filter方法相连接。findFirst和findAny都返回一个可选对象。
Mapping:流支持映射方法,传递一个函数对象作为方法,把流中的元素转换成另一种类型。这种方法应用于单个元素,将其映射成新元素。
Reducing:把数据源中的所有元素结合起来提供单一的值。
Collectors:目前为止你所了解的方法都是返回另一个流或者一个像boolean,int类型的值,或者返回一个可选对象。相比之下,collect方法是一个结束操作,它可以使流里面的所有元素聚集到汇总结果。
传递给collect方法参数是一个java.util.stream.Collector类型的对象。Collector对象实际上定义了一个如何把流中的元素聚集到最终结果的方法。最开始,工厂方法Collectors.toList()被用来返回一个描述了如何把流转变成一个List的Collector对象。后来Collectors类又内建了很多相似的collectors变量。
二、实例
通过一个实例来对比下Java8之前的集合操作:
public class Demo {
private int id;
private String name;
private double invoice;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getInvoice() {
return invoice;
}
public void setInvoice(double invoice) {
this.invoice = invoice;
}
}
public class StreamDemo {
private static Logger logger = LoggerFactory.getLogger(StreamDemo.class);
public static void main(String[] args){
List<Integer> idList1 = getIdsWithOldWay(convertData());
logger.info("以普通的Java实现方式:list={}",idList1);
List<Integer> idList2 = getIdsWithStream(convertData());
logger.info("以Java8的实现方式:list={}",idList2);
boolean flag = idList1.equals(idList2);
logger.info("比较两种方式:compareResult={}",flag);
}
private static List<Integer> getIdsWithOldWay(List<Demo> demoList){
List<Integer> idList = Lists.newArrayList();
List<Demo> demos = Lists.newArrayList();
for(Demo demo : demoList){
if("张三丰".equals(demo.getName())){
if(demo.getInvoice() > 0.00){
demos.add(demo);
}
}
}
Collections.sort(demos, new Comparator<Demo>() {
@Override
public int compare(Demo demo1, Demo demo2) {
return Double.compare(demo1.getInvoice(),demo2.getInvoice());
}
});
for(Demo demo : demos){
idList.add(demo.getId());
}
return idList.subList(0,3);
}
private static List<Integer> getIdsWithStream(List<Demo> demoList){
return demoList.stream()
.filter(demo -> "张三丰".equals(demo.getName()))
.filter(demo -> demo.getInvoice() > 0.00)
.sorted(Comparator.comparingDouble(Demo :: getInvoice))
.map(Demo :: getId)
.limit(3)
.collect(Collectors.toList());
}
private static List<Demo> convertData(){
List<Demo> demoList = Lists.newArrayList();
Demo demo1 = new Demo();
demo1.setId(1);
demo1.setName("张三丰");
demo1.setInvoice(0.03);
demoList.add(demo1);
Demo demo2 = new Demo();
demo2.setId(2);
demo2.setName("张三丰");
demo2.setInvoice(0.02);
demoList.add(demo2);
Demo demo3 = new Demo();
demo3.setId(3);
demo3.setName("张三丰");
demo3.setInvoice(0.01);
demoList.add(demo3);
Demo demo4 = new Demo();
demo4.setId(4);
demo4.setName("张三丰");
demo4.setInvoice(0.06);
demoList.add(demo4);
Demo demo5 = new Demo();
demo5.setId(5);
demo5.setName("张三丰");
demo5.setInvoice(0.05);
demoList.add(demo5);
Demo demo6 = new Demo();
demo6.setId(6);
demo6.setName("张三丰");
demo6.setInvoice(0.04);
demoList.add(demo6);
return demoList;
}
}
三、运行结果
14:27:26.518 [main] INFO c.pajk.docplatformmanager.StreamDemo - 以普通的Java实现方式:list=[3, 2, 1]
14:27:26.618 [main] INFO c.pajk.docplatformmanager.StreamDemo - 以Java8的实现方式:list=[3, 2, 1]
14:27:26.618 [main] INFO c.pajk.docplatformmanager.StreamDemo - 比较两种方式:compareResult=true