1、stream处理数据介绍
如果没有集合Collection
你如何处理数据?几乎所有的Java
应用程序都要使用Collection
处理数据。他们是十分重要的编程工作:例如,您可能想要创建银行交易的集合Collection
,这个集合代表客户的状态。然后,你可能要处理整个集合来找出的顾客花了多少钱。尽管集合如此重要,但是在Java中处理集合的技术还远远不够完善。
首先,对集合的典型处理模式是类似SQL操作,比如"finding
"(例如,找到具有最高价的交易)或"grouping
" (例如,将相关的杂物所有交易合并组) 。大多数数据库让您这样的声明方式指定的操作。例如,下面的SQL查询可以让您找到交易ID最高值: "SELECT id, MAX(value) from transactions
" 。
正如你所看到的,我们并不需要实现如何具体计算最大值(例如,使用循环和一个变量来跟踪的最高值) 。我们只能表达我们所期望的要求(获得最大值)。只要我们显式发出这样的查询,数据库就会为我们去处理。可是为什么我们不能在集合Collection
中实现类似的东西?多少次,你会发现自己使用循环一遍又一遍的反复遍历集合?(如:for Object o: myLists{ ..}
)
其次,如何才能有效地处理真正的大数据集合?在理想的情况下,你想利用多核架构加快处理,。然而,编写并行代码很难而且容易出错。
Java 8 API
的设计者重新提出了一个新的抽象称为流Stream
,可以让你以一种声明的方式处理数据。此外,数据流可以充分利用多核架构而无需编写多线程的一行代码。这听起来不错,不是吗?
首先,我们看看在Java 7
中,如何发现typegrocery
的所有交易,然后返回以交易值降序排序好的交易ID集合?
List<Transaction> groceryTransactions = new Arraylist<>();
for(Transaction t: transactions){
if(t.getType() == Transaction.GROCERY){
groceryTransactions.add(t);
}
}
Collections.sort(groceryTransactions, new Comparator(){
public int compare(Transaction t1, Transaction t2){
return t2.getValue().compareTo(t1.getValue());
}
});
List<Integer> transactionIds = new ArrayList<>();
for(Transaction t: groceryTransactions){
transactionsIds.add(t.getId());
}
而在Java 8使用Stream:
List<Integer> transactionsIds =
transactions.stream()
.filter(t -> t.getType() == Transaction.GROCERY)
.sorted(comparing(Transaction::getValue).reversed())
.map(Transaction::getId)
.collect(toList());
我们首先从交易数据列表中获得一个stream
对象,这是通过使用List
的stream()
方法实现的,下一步几个操作 (filter
, sorted
, map
, collect
)是以链式排列成一个管道,形成对数据的查询操作。
那么这段代码如何并行操作呢?那么只要更换transactions.stream()
为transaction.parallelStream()
即可,请注意lambda
表达式(t-> t.getCategory() == Transaction.GROCERY)
和方法引用(Transaction::getId)
将在下一章详细讲解。
2、Stream起步
首先,定义一下Stream
,Stream
是一个来自支持聚合操作源的元素的序列。
如下特点:
- 1. 所谓元素的序列,一个
Stream
向外提供了一个这样的接口:特定元素类型的值的序列,但是Stream
并不实际持有也就是存储这些元素,它们是在有需求时才会被计算。 - 2. 源:以提供
Stream
进行计算消费的源,这些源有Collection
集合Array
数组或I/O资源等。 - 3.聚合操作:
Stream
支持类似SQL操作和函数式编程的大部分操作,比如:filter, map, reduce, find, match, sorted。
此外,Stream操作不同于Collection操作有两个根本的地方:
- 1.管道Pipelining: 许多流
Stream
操作返回流Stream
自身,这就允许对其操作可以像链条一样排列,变成一个管道,这其中也会激活比如懒加载和short-circuiting
操作。 - 2.内部迭代:相比于集合
Collection
是显式迭代(需要我们编码完成迭代),Stream
操作是在其内部完成迭代操作。
下面我们看看前面Stream代码的内部工作流程:
我们首先从交易transactions
这个列表中获得Stream
对象,数据源就是交易列表,其中提供了一系列元素集合,这些元素都是供Stream
操作的,下一步,我们使用了一系列对这个Stream
的聚合操作,包括过滤filter (用我们指定的谓词条件predicate
过滤元素,也就是代码t -> t.getType() == Transaction.GROCERY)
, 排序(用指定的比较器comparator
对元素进行排序), 以及 map (为了释放获取信息). 所有这些操作除了最后的collect
操作,都是返回一个Stream
对象,这样就能被前后链接在一起变成一个长的管道,可以看成是基于源数据集合的一个查询操作。如同SQL基于数据表的有条件查询语句一样。
最后到collect
被调用操作, collect
操作开始处理这个管道以返回一个结果,不再是一个结果流了,这里一个结果是List一个对象,我们看到collect
接受一个recipe
输入函数然后累计Stream
中元素到一个汇总结果,这里输入函数是toList(),
它是一个将将Stream
转换为List对象。
3、Stream与Collection比较
Stream
和Collection
集合有什么区别?Collection
是关于静止的数据结构,而Stream
是有关动词算法和计算的。前者是主要面向内存,存储在内存中,后者主要是面向CPU,通过CPU实现计算的。
举例将一个影片存储在DVD盘上,这是一个集合,因为它包含整个电影的字节数据结构,而这个影片被放在互联网上,我们通过视频软件去观看它时,它实际是被流化了,它变成了一个字节流,流是与时间有关的概念,而数据结构是与时间无关,不会随着时间变化变化,流正好相反,随着时间不断地动态变化,如同水流一样潺潺不断。
所以,集合与流的主要区别是是否需要被计算,集合是一个内存数据结构,集合中每个元素在加入到集合之前已经被计算了,相反,流是在即时要求即时计算。
使用集合需要开发者主动去遍历,使用一个遍历循环,这称为外部遍历。
而使用一个流库使用内部遍历,它自己为你遍历元素,然后将结果保存在某处,你只要提供一个函数,它就会用这个函数对元素处理完成。内外遍历的区别如下代码:
List<String> transactionIds = new ArrayList<>();
for(Transaction t: transactions){
transactionIds.add(t.getId()); //外部遍历
}
List<Integer> transactionIds =
transactions.stream()
.map(Transaction::getId) //内部遍历
.collect(toList());
到此这篇关于用Java8 stream
处理数据的文章就介绍到这了,更多相关 stream处理数据内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!