Stream 的使用是Lambda应用的一个常见的场景,流的引入使得我们可以站在更高的抽象层次上对集合进行操作.
Stream是一个接口中提供的方法参数大都是函数接口。
大多数常见方法返回值为Stream,可以对数据进行链式连续操作,也即函数式编程,对数据的处理逻辑十分清晰。
在Stream这一知识点下,也有一些十分高级的用法,如支持自定义收集器等,
我对其的理解和使用还停留在一个比较低的水平,所以下面的解释也都比较浅显易懂,
Stream从外部迭代到内部迭代
从一个例子说起
int count = 0;
for(Animal animal:allAnaimals){
if("dog".equals(animal.type)){
count++;
}
}
单一的for循环理解起来还是十分简单,但当多重循环进行嵌套,for循环和while循环混合在一起,则又是另一番天地了。
就其背后的原理来看,for 循环其实是一个封装了迭代的语法糖,我们在这里多花点时间, 看看它的工作原理。首先调用 iterator 方法,产生一个新的 Iterator 对象,进而控制整个迭代过程,这就是外部迭代。迭代过程通过显式调用 Iterator 对象的 hasNext 和 next 方法完成迭代。解释代码如下
int count = 0;
Iterator<Animal> it = allAnimals.iterator();
while(it.hasNext()){
Animal animal = it.next();
if("dog".equals(animal.type)){
count++;
}
}
使用stream的表达方式
int count = allAnimals.stream().filter(animal -> "dog".equals(animal.type)).count();
stream() 方法的调用,它和调用 iterator() 的作用一样。该方法不是返回一个控制迭代的 Iterator 对象,而是返回内部迭代中的相应接口:Stream
Stream常用方法
filter
从一堆数据中基于一定的条件筛选出自己的目标数据,基本数据类型,复杂类型都可
从数据中筛选出大于5的数
@Test
public void test01(){
List<Integer> nums = Arrays.asList(1,2,10,5,6,7,8,9);
List<Integer> collect = nums.stream().filter(x -> x > 5).collect(Collectors.toList());
collect.forEach(item -> System.out.println(item));
}
map
一种映射操作,就相当于函数 f(x)=y,根据自己的逻辑,将某一数据转换成另一种数据类型,基本数据类型之间,复杂数据类型之间,均可
将map中的每一个元素映射为它的值,并对它进行求和
@Test
public void test02(){
Map<String,Integer> map = new HashMap<>();
for(int i=0;i<10;i++){
map.put(String.valueOf(i),i);
}
Integer reduce = map.entrySet().stream().map(node -> node.getValue()).reduce(0, (acc, num) -> acc + num);
System.out.println(reduce);
}
flatMap
将经过map操作后的数据再进行一次映射,通常可以用来提取复杂数据的某个属性来代替这个数据
将两个list的流转换为一个流,然后统计0的个数
@Test
public void test03(){
long count = Stream.of(Arrays.asList(0, 0, 0), Arrays.asList(1, 2, 3, 4, 5)).flatMap(nums -> nums.stream()).filter(x -> x == 0).count();
System.out.println(count);
}
reduce
一种高级的数据操作,用它可以实现其他的函数,如max,min,count等
Optional<T> reduce(BinaryOperator<T> accumulator);
该方法的参数BinaryOperator也是函数式接口,所以支持lambda表达式,用来完成需要的逻辑操作,返回值为Optional类型,T与处理的数据类型保持一致。
T reduce(T identity, BinaryOperator<T> accumulator);
该方法与上面的方法基本相同,不同的是多了一个T identity,常被用来作为计算前的初值,参与逻辑处理
<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner);
第三种方法是上面方法的增强版,上面两种方法返回的值类型与处理的数据类型要保持一致,第三种方法支持类型的转换,如多个数相加,超过了int的范围,这个时候就可以使用第三种,将第一参数设为long类型,这样就不会溢出了可以得到正确的结果,而另外两种方法就不支持。
下面是使用reduce方法的测试
第一种reduce实现最大,最小,累计操作
@Test
public void test06(){
Optional<Integer> max = Stream.of(1, 2, 3, 4, 5).reduce((a, b) -> a+b);
// 这个是累计,取最大 a+b 改为 a>b?a:b 取最小 a<b?a:b
System.out.println(max);
}
第二种reduce,实现带有初值参与的累计操作
@Test
public void test05(){
int i = Stream.of(1, 2, 3, 4, 5).reduce(0, (a, b) -> a * b);
}
这里初值为0,也就是正常的累加操作
第三种reduce,实现类型的转换
@Test
public void test07(){
Long reduce = Stream.of(Integer.MAX_VALUE, Integer.MAX_VALUE).reduce(0L, (a, b) -> a + b, (a, b) -> null);
}
max
从一组数据中,取出基于某个评价指标的最大值
min
从一组数据中,取出基于某种评价指标的最小值
max和min方法演示
@Test
public void test04(){
// optional 是final 不可修改
// 使用comparator.comparing() 进行比较
Optional<Integer> min = Stream.of(1, 2, 3, 4, 5, 0,-1).min(Comparator.comparing(i->i));
Optional<Integer> max = Stream.of(1, 2, 3, 4, 5, 0,-1).max(Comparator.comparing(i->i));
System.out.println(min);
System.out.println(max);
// 使用lambda表达式
Optional<Integer> min1 = Stream.of(1, 2, 3).min((a, b) -> a - b);
System.out.println(min1);
}
collect
List<String> collected = Stream.of("a", "b", "c") n
.collect(Collectors.toList());
使用 collect(toList()) 方法从 Stream 中生成一个列表
collect() 支持多种收集器(可以根据数据的特点进行自定义收集),属于进阶知识,这里不详细展开。
其他知识点(不详细展开了,大家感兴趣,可以自己查阅资料学习)
方法引用
一种推荐使用的表示方法,类名::方法名 如String::length
惰性求值
最终不产生新集合的方法叫作惰性求值方法;而像 count 这样或者为空最终会从 Stream 产生值的方法叫作及早求值方法。
并行化流操作
Stream 并发操作
········