初识stream
需求:如果要发现 type 为 grocery 的所有交易,然后返回以交易值降序排序好的交易 ID 集合
一般写法
//先获取集合
List 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());
}
});
//拿到Id
List transactionIds = new ArrayList<>();
for(Transaction t: groceryTransactions){
transactionsIds.add(t.getId());
}
stream写法
List transactionsIds = transactions.parallelStream().
filter(t -> t.getType() == Transaction.GROCERY).
sorted(comparing(Transaction::getValue).reversed()).
map(Transaction::getId).
collect(toList());
stream概括
Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更个高级版本的 Iterator。
原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作,
比如 “过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,
做出相应的数据转换。
Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,
就好比流水从面前流过,一去不复返。而和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。
顾名思义,当使用串行方式去遍历时,每个 item 读完后再读下一个 item。而使用并行去遍历时,数据会被分成多个段,
其中每一个都在不同的线程中处理,然后将结果一起输出。Stream 的并行操作依赖于 Java7 中引入的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程。
流的构成
当我们使用一个流的时候,通常包括三个基本步骤:
获取一个数据源(source)→ 数据转换→执行操作获取想要的结果,每次转换原有 Stream 对象不改变,返回一个新的 Stream
对象(可以有多次转换),这就允许对其操作可以像链条一样排列,变成一个管道,如下图所示。
需要注意的是,对于基本数值型,目前有三种对应的包装类型 Stream:
IntStream、LongStream、DoubleStream。当然我们也可以用 Stream、Stream >、Stream
,但是 boxing 和 unboxing 会很耗时,所以特别为这三种基本数值型提供了对应的 Stream。
Java 8 中还没有提供其它数值型 Stream,因为这将导致扩增的内容较多。而常规的数值型聚合运算可以
通过上面三种 Stream 进行。
随手小例子
package com.bobo.Lambda.stream;
import com.bobo.Lambda.entity.Person;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* 学习stream
* @author [email protected]
*/
public class StreamTest1 {
public static void main(String[] args) {
List phpProgrammers = new ArrayList() {
{
add(new Person("Jarrod", "Pace", "PHP programmer", "male", 34, 1550));
add(new Person("Clarette", "Cicely", "PHP programmer", "female", 23, 1200));
add(new Person("Victor", "Channing", "PHP programmer", "male", 32, 1600));
add(new Person("Tori", "Sheryl", "PHP programmer", "female", 21, 1000));
add(new Person("Osborne", "Shad", "PHP programmer", "male", 32, 1100));
add(new Person("Rosalind", "Layla", "PHP programmer", "female", 25, 1300));
add(new Person("Fraser", "Hewie", "PHP programmer", "male", 36, 1100));
add(new Person("Quinn", "Tamara", "PHP programmer", "female", 21, 1000));
add(new Person("Alvin", "Lance", "PHP programmer", "male", 38, 1600));
add(new Person("Evonne", "Shari", "PHP programmer", "female", 40, 1800));
}
};
List collect = phpProgrammers.stream().filter(person -> {
return person.getAge() > 30;
})
.collect(Collectors.toList());
//排序
Collections.sort(collect,(person1,person2) -> {
return person1.getAge() - person2.getAge();
});
//循环遍历
collect.forEach(person -> {
System.out.println(person);
});
//整合在一起的写法
phpProgrammers.stream().filter(person -> {
return person.getAge() >30;
}).sorted((person1,person2) -> {
return person1.getAge() - person2.getAge();
}).forEach(person -> {
System.out.println(person);
});
};
}
结果
stream特性
不是数据结构
它没有内部存储,它只是用操作管道从 source(数据结构、数组、generator function、IO channel)抓取数据。
它也绝不修改自己所封装的底层数据结构的数据。例如 Stream 的 filter 操作会产生一个不包含被过滤元素的新 Stream,而不是从 source 删除那些元素。
所有 Stream 的操作必须以 lambda 表达式为参数
不支持索引访问
你可以请求第一个元素,但无法请求第二个,第三个,或最后一个。不过请参阅下一项。很容易生成数组或者 List
惰性化
很多 Stream 操作是向后延迟的,一直到它弄清楚了最后需要多少数据才会开始。
Intermediate 操作永远是惰性化的。
并行能力 当一个 Stream 是并行化的,就不需要再写多线程代码,所有对它的操作会自动并行进行的。
可以是无限的
集合有固定大小,Stream 则不必。limit(n) 和 findFirst() 这类的 short-circuiting 操作可以对无限的 Stream 进行运算并很快完成。