1 概述
Java8中有两大最为重要的改变。第一个是Lambda 表达式;另外一个则是Stream API。
Stream API ( java.util.stream)把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充,因为Stream API可以极大提供Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
Stream 是Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用SQL 执行的数据库查询。也可以使用Stream API 来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
就是用来处理集合、数组的API,集合讲的是数据,而流是计算。
注意:
①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
2 运行机制
Stream分为源source , 中间操作,终止操作。
- 创建Stream
一个数据源(如:集合、数组),获取一个流 - 中间操作
一个中间操作链,对数据源的数据进行处理
一个流可以有0~N个中间操作,每一个中间操作都会返回一个新的流,方便下一个操作使用
一个流只能有一个终止操作
中间操作也称为转换算子-transformation - 终止操作(终端操作)
一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用
Stream只有遇到终止操作,它对策数据源才会开始执行遍历等操作
终止操作也称为动作算子
因为动作算子的返回值不再是Stream,所以这个计算就终止
只有碰到动作算子的时候,才会真正的计算
3 创建Stream
(1)通过数组 Stream.of(数组对象);
(2)通过集合 集合对象.stream();
(3)通过Stream.generate(); 方法来创建
- 通过这种方式创建的流,无限大,在操作的时候,最好使用limit进行最大数量限制
- generate的参数是 Supplier,只有一个get方法,是无参有返回值的
- get方法的返回值,作为整个集合中的数据
(4)通过Stream.iterate();方法来创建
- 这是一个无限流,无限大,通过这种方式创建的流,在操作的时候,最好使用limit进行最大数量限制
- 比如:Stream.iterate(1, x->x+2);
第一个参数是起始值,第二个参数是UnaryOperator 继承了Function 所以有参有返回值,1 就是起始值, x+2 就是步长,类似于一个死循环,起始值是1,步长是2,终止条件是true
(5)已有类的Stream源生成API
- 比如:String str = “abc”;
IntStream chars = str.chars();
public static void main(String[] args) {
// 1 通过数组 Stream.of(数组对象)
String[] strings = {
"a", "b", "c", "d" };
Stream<String> stream1 = Stream.of(strings);
// 2 通过集合
List<String> strings2 = Arrays.asList(strings);
Stream<String> stream2 = strings2.stream();
// 3 通过Stream.generate 方法来创建
Stream<Integer> generate = Stream.generate(() -> 1);
// 通过limit限制最多元素个数
generate.limit(5).forEach(x -> System.out.println(x));
// 4 通过Stream.iterate 方法来创建
Stream<Integer> iterate = Stream.iterate(1, x->x+2);
iterate.limit(5).forEach(x->System.out.println(x));
// 5 已有类的Stream源生成API
String str = "abc";
IntStream chars = str.chars();
chars.forEach(x->System.out.println(x));
}
4 常用的转换算子
- filter : 对元素进行过滤,不符合条件的,就舍弃
- distinct : 去重
- skip : 跳过多少个元素
- limit : 取一个集合中的前几条数据
- map : 可以理解为是在遍历集合的过程中,对元素进行操作,比如进行判断,集合元素是否大于4 ,返回值为boolean类型,或者对集合元素进行更改,比如每个元素都自身+1
- flatMap : 解决一个字符串数组,返回单一的字符串使用flatMap
注意 只有这些算子是不会真正进行计算的,只有调用动作算子,才会真正计算。
public static void main(String[] args) {
String[] arr = {
"a", "b", "c", "a" };//创建字符串数组
List<String> strings = Arrays.asList(arr);//数组转为集合
// 利用集合创建流
Stream<String> stream = strings.stream();
/**
* filter : 对元素进行过滤,不符合条件的,就舍弃
*
* collect : 动作算子,把符合条件元素的转换为集合
*/
// 只要a
List<String> value = stream.filter(x -> x.equals("a")).collect(Collectors.toList());
System.out.println(value);//[a, a]
// 注意:stream在使用后 必须重新生成
// 利用集合重新创建流
stream = strings.stream();
/**
* skip : 跳过n个元素,比如原来是abca 跳过1个 就是bca
*/
value = stream.skip(1).collect(Collectors.toList());
System.out.println(value);//[b, c, a]
// 重新生成流
stream = strings.stream();
// 如果元素是a就返回true,否则就返回false,把返回结果形成一个新的集合
List<Boolean> value1 = stream.map(x -> x.equals("a")).collect(Collectors.toList());
System.out.println(value1);//[true, false, false, true]
// 重新生成流
stream = strings.stream();
// 更改集合元素
value = stream.map(x -> x + "---").collect(Collectors.toList());
System.out.println(value);//[a---, b---, c---, a---]
// 重新生成流
stream = strings.stream();
value = stream.distinct().collect(Collectors.toList());
System.out.println(value);// [a, b, c]
// 重新生成流
stream = strings.stream();
//limit():取前几条数据
value = stream.limit(2).collect(Collectors.toList());
System.out.println(value);// [a, b]
String[] arr1 = {
"1,2,3","a,b,c"};
strings = Arrays.asList(arr1);
// 现在集合中有两个数据,分别是 "1,2,3" 和 "a,b,c"
// 把数组中每一个元素都拿出来,分别以 , 逗号分割,形成两个字符串数组
// 最终通过 collect 合并到一起, 得到 1,2,3,a,b,c 6个元素
stream = strings.stream();//生成流
value = stream.map(x->x.split(",")).flatMap(array->Arrays.stream(array)).collect(Collectors.toList());
System.out.println(value);// [1, 2, 3, a, b, c]
}
5 常用的动作算子
- forEach : 循环
- 计算 : min,max,count,average
- 匹配 : anyMatch,allMatch,noneMatch,findFirst,findAny
- 汇聚 : reduce
- 收集器 : collect
public static void main(String[] args) {
String[] arr = {
"a","b","c"}; //创建集合
List<String> strings = Arrays.asList(arr);//转换为数组
Stream<String> stream = strings.stream();//生成流
// 测试forEach
// 先只要 a ,然后输出
stream.filter(x->x.equals("a")).forEach(x->System.out.println(x));
// 测试 count
//重新生成流
stream= strings.stream();
// 获取多少个数据
// 如果只想获取条数,可以调用集合的 size方法,更简单
// 只是有动作算子价值不大,一般和转换算子一起使用,才能体现优势
long count = stream.count();
System.out.println(count);//3
// collect:收集器
// 把结果组织成集合
stream= strings.stream();//重新生成流
// 修改元素
List<String> list = stream.map(x->x+"==").collect(Collectors.toList());
System.out.println(list);// [a==, b==, c==]
}