第十四章 Stream API

概述

从JDK1.8开始,Java语言引入了一个全新的流式Stream AP,StreamAP|把真正的函数式编程风格运用到ava语言中,使用StreamAP(可以帮我们更方便地提作集合,允许开发人员在不改变原始数据源的情况下对集合进行操作,这便得代码更加简洁、易读和可维护。

使用Stream AP|对集合数据进行操作,就类似于使用SQL执行的数据库查询,也可以使用treamAP)来并行执行的操作。简而言之,Stream API提供了一种高效且易于使用的处理数据的方式。

  • 操作步骤
  1. 第一步:创建Stream
    通过数据源(如:集合、数组等)来获取一个Stream对象。
  2. 第二步:中间操作
    对数据源的数据进行处理,该操作会返回一个Stream对象,因此可以进行链式操作。
  3. 第三步:终止操作
    执行终止操作时,则才会真正执行中间操作,并且并返回一个计算完毕后的结果。
  • 重要特点
  1. Stream自己不会存储元素,只能对元素进行计算
  2. Stream不会改变数据对象,反而可能会返回一个持有结果的新Stream。
  3. Stream上的操作属于延迟执行,只有等到用户真正需要结果的时候才会执行
  4. Stream一旦执行了终止操作,则就不能再调用其它中间操作或终止操作了

创建Stream对象

  • 通过Collection接口的Stream()方法来获取Stream对象
// 获取Stream 单线程
ArrayList<Integer> myList = new ArrayList<>();
myList.add(1);
myList.add(2);
Stream<Integer> myListStream = myList.stream();

// 获取并行Stream 底层启用多核,在数据量非常大是时候使用
HashSet<Integer> mySet = new HashSet<>();
mySet.add(1);
mySet.add(2);
Stream<Integer> mySetStream = mySet.parallelStream();
  • 通过Arrays数组工具类的Stream()方法
String[] names = {"zhangsan", "lisi", "wangwu"};
Stream<String> stream1 = Arrays.stream(names);

int[] nums = {1, 2, 3};
IntStream stream2 = Arrays.stream(nums);
  • 通过Stream接口本身的of(可变长度参数)方法
Stream<Integer> integerStream = Stream.of(1, 2, 3);
Stream<String> stringStream = Stream.of("hello", "world", "hello world");
顺序流和并行流

在前面获得Stream对象的方式,我们都称之为“顺序流”,顺序流对Stream元素的处理是单线程的,即一个一个元素进行处理,处理数据的效率较低。
如果Stream流中的数据处理没有顺序要求,并且还希望可以并行处理Stream的元素,那么就可以使用“并行流“来实现,从而提高处理数据的效率。
一个普通stream转换为可以并行处理的stream非常简单,只需要用调用Stream提供的paralel()方法进行转换即可,这样就可以并行的处理Stream的元素。那么,我们不需要编写任何多线程代码就可以享受到并行处理带来的执行效率的提升。

// 创建一个Stream顺序流
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
// 验证stream是否为并行流
System.out.println(stream.isParallel()); // false
// 将Stream转换为并行流
Stream<Integer> parallelStream = stream.parallel();
// 验证是否还是原来的流
System.out.println(stream == parallelStream); // true
// 验证stream是否为并行流
System.out.println(stream.isParallel()); // true

Stream API 的中间操作

  • 筛选(filter)
  • 映射(map)
  • 除重(distinct)
  • 排序(sort)
  • 合并(concat)
  • 截断和跳过(limit skip)
ArrayList<String> list = new ArrayList<>();
list.add("Hello");
list.add("World");
list.add("Java");
list.add("Python");
list.add("C++");
list.add("JavaScript");
list.add("Python");
// 获取stream
Stream<String> stream1 = list.stream();
stream1 = stream1
        // 过滤 不包含C++
        .filter(s -> !"C++".equals(s))
        // 全部转换为大写
        .map(String::toUpperCase)
        // 去重
        .distinct()
        // 排序
        .sorted()
        // 截断
        .limit(3)
        // 跳过
        .skip(2);

Stream<String> stream2 = Stream.of("11", "22", "33", "44");
// 合并 调用Stream的静态方法
Stream.concat(stream1, stream2).forEach(System.out::println);

Stream API 的终止操作

触发终止操作时才会真正执行中间操作,终止操作执行完毕会返回计算的结果,并且终止操作执行完毕那么操作的stream就失效,也就是不能再执行中间操作或终止操作了。

  • 遍历(forEach)
stream1.forEach(System.out::println);
  • 匹配(match)
// 检查是否全部匹配
stream1.allMatch(s -> s.equals("Hello"));
// 检查是否至少匹配一个
stream1.anyMatch(s -> s.equals("World"));
// 检查是否一个都不匹配
stream1.noneMatch(s -> s.equals("Java"));
  • 归约(reduce)
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5);
// 第一个参数 初始化默认值,返回Integer时需指定,返回 Optional ,后续使用Optional.get()获取拼接结果
// 第二个参数 接口函数第一个参数为上一次的结果,第二个参数为当前元素
Integer reduce = integerStream.reduce(0, (a, b) -> a + b);
System.out.println(reduce);

Optional<String> reduce = stream1.reduce((a, b) -> a + b);
reduce.ifPresent(System.out::println);
  • 收集(collect)

收集(collect),可以说是内容最繁多、功能最丰富的部分了。从字面上去理解,就是把一个流收集起来,最终可以是收集成一个值也可以收集成一个新的集合。
调用Stream接口提供的"<R,A>R collect(Collector<? superT,A,R>collector;"方法来实现收集操作,并且参数中的Collector对象大都是直接通过Collectors工具类获得,实际上传入的Collector决定了collect()的行为。

  1. 收集 归集(toList/toSet/toMap)

因为Stream流不存储数据,那么在Stream流中的数据完成处理后,如果需要把Stream流的数据存入到集合中,那么就需要使用归集的操作。在Colectors提供的toList、toSet和toMap比较常用,另外还有Collectors提供的toCollection等比较复杂一些的用法。

List<String> list = Arrays.asList("a", "b", "c");
// 将Stream转为list集合
List<String> collectList = list.stream().collect(Collectors.toList());
// 将Stream转为set集合
Set<String> collectSet = list.stream().collect(Collectors.toSet());
// 将Stream转为map集合
Stream<String> stringStream = Stream.of("a:a1", "b:b1", "c:c1");
Map<String, String> collectMap = stringStream.collect(Collectors.toMap(
        // 2个回调参数 第一个为map的key 第2个为map的value
        s -> s.substring(0, s.indexOf(":")),
        s -> s.substring(s.indexOf(":") + 1)
));
  1. 收集 统计(count/averaging)

Collectors提供了一系列用于数据统计的静态方法:
1.计数:counting
2.平均值:averagingInt、averagingLong、averagingDouble
3.最值:maxBy、minBy
4.求和:summingInt、summingLong、summingDouble
5.统计以上所有:summarizingInt、summarizingLong、summarizingDouble

ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
// 获取元素个数 方式1
System.out.println((Long) list.stream().count());
// 获取元素个数 方式2
System.out.println(list.stream().collect(Collectors.counting()));
// 获取平均数
System.out.println(list.stream().collect(Collectors.averagingDouble(Integer::intValue)));
// 获取最大数 方式1
System.out.println(list.stream().max(Integer::compareTo));
// 获取最大数 方式2
System.out.println(list.stream().collect(Collectors.maxBy( Integer::compareTo )));
// 获取和 方式1
System.out.println((Double) list.stream().mapToDouble(Integer::intValue).sum());
// 获取和 方式2
System.out.println(list.stream().collect(Collectors.summingDouble( Integer::intValue )));
// 获取所有信息 IntSummaryStatistics{count=4, sum=10, min=1, average=2.500000, max=4}
System.out.println(list.stream().collect(Collectors.summarizingInt( Integer::intValue )));
  1. 收集 分组(groupingBy)

分组(groupingBy),将Stream按条件分为两个Map,比如按照学生年龄分为两个Map集合。

ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(1);
list.add(1);
list.add(2);
list.add(3);
list.add(3);
list.add(3);

// 分组
Map<Integer, List<Integer>> collect = list.stream().collect(Collectors.groupingBy(e -> e ));
// {1=[1, 1, 1], 2=[2], 3=[3, 3, 3]}
System.out.println(collect);
  1. 收集 接合(joining)

接合(joining),把Stream计算的数据按照一定的规则进行拼接。

ArrayList<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");

// 接合拼接 方式1
String.join(" - ", list);
// 接合拼接 方式2
String collect = list.stream().collect(Collectors.joining(" - "));
// A - B - C
System.out.println(collect);
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容