Stream(流)

什么是Stream

Java8添加了一个新的接口Stream(流),可以用函数式风格对数据进行处理。就像写sql语句一样。

流处理过程

+--------+     +--------+     +--------+ 
| 创建流  |+--->| 处理流  |+--->| 终止流  |
+--------+     +--------+     +--------+ 

流处理特点: - 不保存数据 - 不修改数据 - 延迟处理,在获得结果的时候才会执行中间数据处理操作

流类型 - stream() − 创建串行流。 - parallelStream() − 创建并行流(内部使用Fork/Join线程模型处理)

创建流

流的数据源可以是:集合、数组、迭代器、生产器等

// list转stream
List<String> strList = new ArrayList<>();
Stream<String> listStream = strList.stream();

// 数组转stream
String[] strArr = new String[10];
Stream<String> arrStream = Arrays.stream(strArr);

// 通过静态方法创建流
Stream<String> stream = Stream.of("a", "b", "c");

// 通过种子创建无限流
Stream<Integer> iterStream = Stream.iterate(2, i -> i * i);
/* 
 * 2
 * 4
 * 8
 * 16
 * ...
 */

// 创建10个元素的流
Stream<Integer> iter10Stream = Stream.iterate(2, i -> i * 2).limit(3);
iter10Stream.forEach(System.out::println);
/* 
 * 2
 * 4
 * 8
 */

// 生产
Stream<Double> genStream = Stream.generate(Math::random).limit(3);
genStream.forEach(System.out::println);
/* 
 * 0.5875071685084056
 * 0.622271393744705
 * 0.7873169115032873
 */

处理流

流的处理就像流水线一样,按照流程一步步处理下去

筛选与切片

  • filter(Predicate p) - 过滤掉不符合条件的数据
  • distinct() - 数据去重(使用hashCode和equals判断是否是相同数据)
  • limit(long maxSize) - 截断操作,当已经获得maxSize个数据后,将不再继续筛选数据
  • skip(long n) - 跳过前n个数据,如果数据不足n个,则返回一个空流

映射 - 将数据映射成其他类型的数据

  • map(Function<? super T, ? extends R> mapper) - 将数据映射成新的类型
  • mapToInt(ToIntFunction<? super T> mapper) - 将数据映射成Integer类型
  • mapToLong(ToLongFunction<? super T> mapper) - 将数据映射成Long类型
  • flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) - 将数据映射另外一个流,并将每个数据映射的流合并成一个流

排序

  • sorted() - 字典排序
  • sorted(Comparator<? super T> comparator) - 自定义排序

终止流

从一系列中间操作获得最终数据

匹配

  • allMatch(Predicate<? super T> predicate) - 是否所有元素匹配
  • anyMatch(Predicate<? super T> predicate) - 是否有至少一个数据匹配
  • noneMatch(Predicate<? super T> predicate) - 是否没有数据匹配

查找

  • findFirst() - 返回第一个数据
  • findAny() - 返回任一数据

统计

  • count() - 返回数据总数
  • max(Comparator<? super T> comparator) - 返回数据的最大值
  • min(Comparator<? super T> comparator) - 返回数据的最小值

迭代

  • forEach(Consumer<? super T> action) - 内部跌倒方式获取数据

聚合

  • reduce(BinaryOperator< T> accumulator) - 将数据聚合后获得一个新值,返回值是Optional类型
  • reduce(T identity, BinaryOperator< T> accumulator) - 通过起始值identity将数据聚合后获得获得一个新的值

收集

  • collect(Collector<? super T, A, R> collector) - 通过Collector接口,将数据收集成一个非Stream类型的集合

::: tip 可以用Collectors静态方法来创建Collector对象

  • toList: 将数据转成List
  • toSet: 将数据转成Set
  • toMap: 将数据转成Map
  • averagingDouble: 求数据平均值
  • groupingBy: 对数据进行分组

其他更多方法查看Collectors api :::

示例

public static void main(String[] args) {
    List<String> strList = Arrays.asList("a", " ", "sas", "1", "39", "as234",
        "nba", "cba", "32", "25", "ss ", " srr ", "10", "", " bqwe");

    // 查找出全部数字,按照字典排序,并将结果收集到List<Integer>
    List<Integer> intList = strList.stream()
        .filter(str -> str.trim().length() > 0)
        .filter(str -> isInteger(str))
        .map(str -> Integer.parseInt(str))
        .sorted()
        .collect(Collectors.toList());
        System.out.println(intList);

    // 查找出所有数字中最大的值是多少
    Optional<Integer> max = strList.stream()
        .filter(str -> str.trim().length() > 0)
        .filter(str -> isInteger(str))
        .map(str -> Integer.parseInt(str)).max((a, b) -> a - b);
    System.out.println("max: " + max.get());

    // 查找出所有数字的总和
    Optional<Integer> sum = strList.stream()
        .filter(str -> str.trim().length() > 0)
        .filter(str -> isInteger(str))
        .map(str -> Integer.parseInt(str))
        .reduce((a, b) -> a + b);
    System.out.println("sum: " + sum.get());

    // 查找出所有数字的个数
    long count = strList.stream()
        .filter(str -> str.trim().length() > 0)
        .filter(str -> isInteger(str))
        .map(str -> Integer.parseInt(str))
        .count();
    System.out.println("count: " + count);

    // 查找出所有数字的平均值
    Double ave = strList.stream()
        .filter(str -> str.trim().length() > 0)
        .filter(str -> isInteger(str))
        .map(str -> Integer.parseInt(str))
        .collect(Collectors.averagingInt(value -> value));
    System.out.println("ave: " + ave);

    // 去掉所有字符串两边空格,并获得所有以字母a开头的字符串
    strList.stream()
        .map(str -> str.trim())
        .filter(str -> str.startsWith("a"))
        .filter(str -> !isInteger(str))
        .forEach(System.out::println);
}

public static boolean isInteger(String str) {  
    Pattern pattern = Pattern.compile("^[-\\+]?[\\d]*$");  
    return pattern.matcher(str).matches();  
}