JUC并发编程——Stream 流式计算
1、Stream 简介
Stream 简介
Stream是 Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。
使用Stream API(java.util.stream)对集合数据进行操作,就类似于使用SQL执行的数据库查询。也可以使用Stream API来并行执行操作。
简而言之,Stream API提供了一种高效且易于使用的处理数据的方式。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
中间操作的返回结果都是Stream,故可以多个中间操作叠加;终止操作用于返回我们最终需要的数据,只能有一个终止操作。
Stream(流)是一个来自数据源的元素队列并支持聚合操作
- 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
- 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
- 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
和以前的Collection操作不同, Stream操作还有两个基础的特征:
- Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
- 内部迭代: 以前对集合遍历都是通过 Iterator 或者 For-Each 的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。
注意:
1、Stream自己不会存储元素。
2、Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream。
3、Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”
2、操作 Stream
操作 Stream 三个步骤:
1、创建 Stream
一个数据源(如:集合、数组),获取一个流
2、中间操作
一个中间操作链,对数据源的数据进行处理
3、最终操作
一个终止操作,执行中间操作链,并产生结果
1、创建 Stream 方法
-
可以通过 Collection 系列集合提供的 stream() 【串型流】或 parallelStream() 【并行流】方法
创建代码实例:
ArrayList<Object> list1 = new ArrayList<>(); Stream<Object> stream1 = list1.stream();
-
通过 Arrays 中的静态方法 stream() 获取数组流
创建代码实例:
int[] arr = new int[10]; IntStream stream2 = Arrays.stream(arr);
-
通过 Stream 类中的静态方法 of()
创建代码实例:
Stream<Serializable> stream3 = Stream.of("aa", "bb");
-
静态的Stream.generate()方法生成无限流,接受一个不包含引元的函数
创建代码实例:
Stream<Double> stream5 = Stream.generate(() -> Math.random());
-
静态的Stream.iterate()方法生成无限流,接受一个种子值以及一个迭代函数
创建代码实例:
Stream<Integer> stream4 = Stream.iterate(0, (x) -> x - 2);
2、中间操作
筛选与切片:
- **filter(Predicate predicate):**过滤Stream中所有不符合predicate的元素。
- **limit(long maxSize):**该方法用于保证对该流的后续访问中最大允许访问的元素个数,这是一个有状态的,短路方法。
- **skip(n):**跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。
- **distinct:**通过流所生成元素的 hashCode() 和 equals() 去除重复元素
映射:
- **map:**接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
- flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
排序:
- **sorted():**自然排序(Comparable)
- **sorted(Comparator com):**定制排序(Comparator)
3、最终操作
查找与匹配:
-
allMatch:检查是否匹配所有元素
-
anyMatch:检查是否至少匹配一个元素
-
noneMatch:检查是否没有匹配的元素
-
findFirst:返回流中的第一个元素
-
findAny:返回流中任意一个元素
-
max:返回流中的最大值
-
min:返回流中的最小值
-
count() :返回流中元素的数量。
-
**forEach(Consumer action):**遍历流中所有元素,对每个元素执行action。
归约:
- reduce(T identity,BinaryOperator) / reduce(BinaryOperator):可以将流中元素反复结合起来,得到一个值。
收集:
- collect—将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
Stream 代码实例
package com.cheng.stream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
public class Test {
public static void main(String[] args) {
//创建6个用户
User user1 = new User(1,"a",21);
User user2 = new User(2,"b",22);
User user3 = new User(3,"c",23);
User user4 = new User(4,"d",24);
User user5 = new User(5,"e",25);
User user6 = new User(6,"f",26);
List<User> list = Arrays.asList(user1,user2,user3,user4,user5,user6);
/**
* 要求:从上面用户中,筛选出符合下面五个条件的用户,用一行代码实现
* 1、用户id为偶数
* 2、年龄必须大于23岁
* 3、用户名转换为大写
* 4、用户名字母倒着排序
* 5、只输出一个用户
*/
//Collection接口的stream()方法生成流
list.stream()
//中间操作
.filter((user) -> {return user.getId() % 2 == 0;})
.filter((user) -> {return user.getAge() > 23;}) //将结果为false的元素过滤掉
.map((user) -> {return user.getName().toUpperCase();})//转换元素的值
.sorted((u1,u2)->{return u2.compareTo(u1);})//将流元素按传入的条件排序
.limit(1)//保留第一个元素
.forEach(System.out :: println);//最终操作
}
}