Stream

遍历数据集的高级迭代器。使用StreamApi让代码:

  1. 声明式:更简洁,更易读;
  2. 可复合:更灵活;
  3. 可并行:性能更好;

使用流

流的使用一般包括三件事:

  1. 一个数据源(如集合)来执行一个查询;
  2. 一个中间操作链,形成一条流的流水线;
  3. 一个终端操作,执行流水线,并能生成结果;

流操作

创建操作

创建流

  • 集合

    List<Employee> employees = EmployeeData.getEmployees();
    //        default Stream<E> stream() : 返回一个顺序流
    Stream<Employee> stream = employees.stream();
    //        default Stream<E> parallelStream() : 返回一个并行流
    Stream<Employee> parallelStream = employees.parallelStream();
  • 数组

     int[] arr = new int[]{1,2,3,4,5,6}; 
    //调用Arrays类的static <T> Stream<T> stream(T[] array): 返回一个流
     IntStream stream = Arrays.stream(arr);
  • stream的of()

    Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
  • 文件流

    Java中用于处理文件等I/O操作的NIO API(非阻塞I/O)已更新,以便利用StreamAPI。java.nio.file.Files中的很多静态方法都会返回一个流。例如,一个很有用的方法是Files.lines,它会返回一个由指定文件中的各行构成的字符串流。

    Files.lines得到一个流,其中的每个元素都是给定文件中的一行

    Stream<String> lines = Files.lines(Paths.get("/comprehensive-code/aaaa.txt")
  • 无限流

    • 迭代 Stream.iterate

       Stream.iterate(0,n -> n+2).limit(5).forEach(System.out::println);
    • 生成 Stream.generate

       Stream.generate(Math::random).limit(10).forEach(System.out::println);
    • 由iterate和generate产生的流会用给定的函数按需创建值,因此可以无穷无尽地计算下去!一般来说,应该使用limit(n)来对这种流加以限制,以避免打印无穷多个值。

中间操作

可以连接起来的流操作

筛选

  • filter过滤操作

  • distinct去重操作

    List<Employee> list = EmployeeData.getEmployees();
    //        filter(Predicate p)——接收 Lambda , 从流中排除某些元素。
    Stream<Employee> stream = list.stream();
    //练习:查询员工表中薪资大于7000的员工信息
    stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println);

切片

  • limit

    //        limit(n)——截断流,使其元素不超过给定数量。
    list.stream().limit(3).forEach(System.out::println);
  • skip

    //        skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
    list.stream().skip(3).forEach(System.out::println);
  • takeWhile(Java 9方法)遭遇第一个不符合要求的元素时停止处理

  • dropWhile(Java 9方法)遭遇第一个符合要求的元素时停止处理

映射

  • map

    //        map(Function f)——接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素。
    List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
    list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);
  • flatmap

    //        flatMap(Function f)——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
    Stream<String> stringStream = Stream.of("aaaaa", "vvvvv", "dddddd", "avd");
    List<String> collect = stringStream
            .map(string -> string.split(""))
            .flatMap(Arrays::stream).collect(Collectors.toList());
    System.out.println(collect);

排序

  • sorted

    //        sorted()——自然排序
    List<Integer> list = Arrays.asList(12, 43, 65, 34, 87, 0, -98, 7);
    list.stream().sorted().forEach(System.out::println);
    //        sorted(Comparator com)——定制排序

    List<Employee> employees = EmployeeData.getEmployees();
    employees.stream().sorted( (e1,e2) -> {

     int ageValue = Integer.compare(e1.getAge(),e2.getAge());
     if(ageValue != 0){
         return ageValue;
     }else{
         return -Double.compare(e1.getSalary(),e2.getSalary());
     }

    }).forEach(System.out::println);

终止操作

关闭流的操作

查找

  • findAny

    //        findFirst——返回第一个元素
    Optional<Employee> employee = employees.stream().findFirst();
  • findFirst

    //        findAny——返回当前流中的任意元素
    Optional<Employee> employee1 = employees.parallelStream().findAny();

匹配

  • anyMatch

    //         anyMatch(Predicate p)——检查是否至少匹配一个元素。
    //         练习:是否存在员工的工资大于 10000
    boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000);
  • allMatch

    //        allMatch(Predicate p)——检查是否匹配所有元素。
    //          练习:是否所有的员工的年龄都大于18
    boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18);
  • noneMatch

    //          noneMatch(Predicate p)——检查是否没有匹配的元素。
    //          练习:是否存在员工姓“雷”
    boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("雷"));

求和 最大和最小值

  • count
// count——返回流中元素的总个数
long count = employees.stream().filter(e -> e.getSalary() > 5000).count();
  • max min
//        max(Comparator c)——返回流中最大值
//        练习:返回最高的工资:
Stream<Double> salaryStream = employees.stream().map(e -> e.getSalary());
Optional<Double> maxSalary = salaryStream.max(Double::compare);
System.out.println(maxSalary);
//        min(Comparator c)——返回流中最小值
//        练习:返回最低工资的员工
Optional<Employee> employee = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(employee);

归约

  • redue

    //        reduce(T identity, BinaryOperator)——可以将流中元素反复结合起来,得到一个值。返回 T
    //        练习1:计算1-10的自然数的和
    List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    Integer sum = list.stream().reduce(0, Integer::sum);
    System.out.println(sum);

    //        reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值。返回 Optional<T>
    //        练习2:计算公司所有员工工资的总和
    List<Employee> employees = EmployeeData.getEmployees();
    Stream<Double> salaryStream = employees.stream().map(Employee::getSalary);
    Optional<Double> sumMoney = salaryStream.reduce(Double::sum);
    // Optional<Double> sumMoney = salaryStream.reduce((d1,d2) -> d1 + d2);
    System.out.println(sumMoney.get());

收集

collect方法,主要使用收集器Collector

数值流

减少装箱拆箱操作,Stream API还提供了原始类型流特化,专门支持处理数值流的方法。IntStream、DoubleStream和LongStream

  • 映射到数值流

    • mapToInt
    • mapToDouble
    • mapToLong
  • 转换回对象流

    • boxed
  • 默认值OptionalInt

    Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6);
    OptionalInt max = integerStream.mapToInt(Integer::intValue).max();
    System.out.println(max.orElse(1));

Collectors

Collector接口中方法的实现决定了如何对流执行归约操作。但Collectors实用类提供了很多静态工厂方法,可以方便地创建常见收集器的实例,只要拿来用就可以了。

归约操作

  • Collectors.counting

    // 集合个数
    Long collect = employees.stream().collect(Collectors.counting());
    System.out.println(collect);
    System.out.println(employees.stream().count());
  • Collectors.maxBy 和 Collectors.minBy

    //最大最小值
    Optional<Employee> collect1 = employees.stream()
      .collect(Collectors.maxBy(Comparator.comparingDouble(Employee::getSalary)));
    System.out.println(collect1.get());
    Optional<Employee> collect2 = employees.stream()
      .collect(Collectors.minBy(Comparator.comparingDouble(Employee::getSalary)));
    System.out.println(collect2.get());
  • Collectors.summingInt

    // 汇总
      Integer collect3 = employees.stream().collect(Collectors.summingInt(Employee::getId));
      System.out.println(collect3);

    Collectors.summingLong和Collectors.summingDouble方法的作用完全一样,可以用于求和字段为long或double的情况。

    Collectors.averagingInt,连同对应的averagingLong和averagingDouble可以计算数值的平均数;

    summarizingInt,summarizingLong和summarizingDouble可以计算总和、平均值、最大值和最小值;得到IntSummaryStatistics,LongSummaryStatistics和DoubleSummaryStatistics

  • Collectors.joining

    joining工厂方法返回的收集器会把对流中每一个对象应用toString方法得到的所有字符串连接成一个字符串。

    // 2个重载版本
    List<Employee> employees = EmployeeData.getEmployees();
    String collect = employees.stream().map(Employee::getName)
      .collect(Collectors.joining());
    System.out.println(collect);
    // 添加分隔符
    String collect1 = employees.stream().map(Employee::getName).collect(Collectors.joining(","));
    System.out.println(collect1);
    // 添加分隔符,开头 结尾 
    String collect2 = employees.stream().map(Employee::getName)
      .collect(Collectors.joining(",", "开头", "结尾"));
    System.out.println(collect2);
  • Collectors.reducing

    需要三个参数:

    1.第一个参数是归约操作的起始值,也是流中没有元素时的返回值,所以很显然对于数值和而言0是一个合适的值。

    2.转换函数

    3.是一个BinaryOperator,将两个项目累积成一个同类型的值

    List<Employee> employees = EmployeeData.getEmployees();
      Integer collect = employees.stream().collect(Collectors.reducing(0, Employee::getId, (a, b) -> a + b));
      System.out.println(collect);
    Optional<Employee> collect3 = employees.stream()
      .collect(Collectors.reducing((a, b) -> a.getSalary() > b.getSalary() ? a : b));
    System.out.println(collect3);

    Optional<Integer> collect1 = Stream.of(1, 2, 3, 4, 5).collect(Collectors.reducing((a, b) -> a + b));
    System.out.println(collect1.get());
    Integer collect2 = Stream.of(1, 2, 3, 4, 5).collect(Collectors.reducing(10, (a, b) -> a + b));
    System.out.println(collect2);

分组

Collectors.groupingBy。

List<Employee> employees = EmployeeData.getEmployees();
// 按工资分组
Map<Double, List<Employee>> collect = employees.stream()
  .collect(Collectors.groupingBy(Employee::getSalary));
System.out.println(collect);
Map<String, List<Employee>> collect1 = employees.stream().collect(Collectors.groupingBy(employee -> {
            if (employee.getSalary() > 9000) {
                return "gao";
            } else if (employee.getSalary() > 500) {
                return "zhong";
            } else {
                return "di";
            }
        }));
System.out.println(collect1);
操作分组的元素

Collectors类重载了工厂方法groupingBy,除了常见的分类函数,它的第二变量也接受一个Collector类型的参数。

Map<Double, List<String>> collect2 = employees.stream()
.collect(Collectors.groupingBy(Employee::getSalary, Collectors.mapping(Employee::getName, Collectors.toList())));
System.out.println(collect2);

Collectors类通过mapping方法提供了另一个Collector函数,它接受一个映射函数和另一个Collector函数作为参数。作为参数的Collector会收集对每个元素执行该映射函数的运行结果。

有三个参数重载版本,多增加一个map生成工厂

 HashMap<Double, List<Integer>> collect3 = employees.stream()
   .collect(Collectors.groupingBy(Employee::getSalary, HashMap::new, Collectors
                                  .mapping(Employee::getAge, Collectors.toList())));
多级分组

要实现多级分组,可以使用一个由双参数版本的Collectors.groupingBy工厂方法创建的收集器,它除了普通的分类函数之外,还可以接受collector类型的第二个参数。那么要进行二级分组的话,可以把一个内层groupingBy传递给外层groupingBy,并定义一个为流中项目分类的二级标准。

 // 多级分组
Map<Double, Map<Integer, List<Employee>>> collect4 = employees.stream().collect
(Collectors.groupingBy(Employee::getSalary,Collectors.groupingBy(Employee::getAge)));
按子组收集数据
  • 把收集器的结果转换为另一种类型

    Map<Double, Optional<Employee>> collect5 = employees.stream().
       collect(Collectors.groupingBy(Employee::getSalary, Collectors.maxBy(Comparator.comparing(Employee::getName))));

    // 把收集器的结果转换为另一种类型
    Map<Double, Employee> collect6 = employees.stream().
      collect(Collectors.groupingBy(Employee::getSalary, Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparingInt(Employee::getId)), Optional::get)));   
  • 与groupingBy联合使用的其他收集器

     Map<Double, Set<String>> collect7 = employees.stream().collect(
       Collectors.groupingBy(Employee::getSalary, Collectors.mapping(Employee::getName, Collectors.toSet()))); 

    联合使用其他的收集器,可以产生各种类型数据。继续摸索使用中

分区

分区函数返回一个布尔值,这意味着得到的分组Map的键类型是Boolean,于是它最多可以分为两组——true是一组,false是一组。

 List<Employee> employees = EmployeeData.getEmployees();
 Map<Boolean, List<Employee>> collect = employees.stream()
    .collect(Collectors.partitioningBy(employee -> employee.getSalary() > 1000));

总结


Java8 Strean api的更多相关文章

  1. 何用Java8 Stream API进行数据抽取与收集

    上一篇中我们通过一个实例看到了Java8 Stream API 相较于传统的的Java 集合操作的简洁与优势,本篇我们依然借助于一个实际的例子来看看Java8 Stream API 如何抽取及收集数据 ...

  2. Java8 新API读取文件内容

    import java.io.IOException;import java.nio.charset.Charset;import java.nio.file.Files;import java.ni ...

  3. java8 异步api、循环、日期

    java8 异步api.循环.日期 转载请注明出处:https://www.cnblogs.com/funnyzpc/p/10801470.html 异步api 对于多任务耗时的业务场景,一般我们会用 ...

  4. Java8 日期 API 业务使用

    最近在做账单结算业务,需要根据客户选择的结算方式来推算下一次结算日期以及该次结算日期段. 推算日期这样的业务直男君以前就写过,只不过使用的是熟悉的 java.util.date 和 java.util ...

  5. 如何用Java8 Stream API找到心仪的女朋友

    传统的的Java 集合操作是有些啰嗦的,当我们需要对结合元素进行过滤,排序等操作的时候,通常需要写好几行代码以及定义临时变量. 而Java8 Stream API 可以极大简化这一操作,代码行数少,且 ...

  6. Java8 Time API与老Date之间的转换

    前面我已经总结了Java8 Time API常用的一些方法.封装的工具类,可是最近需要对一个比较老的项目进行重构,大致看了一下使用的Jdk还是7而且里面的时间工具类还是使用的Date和Calendar ...

  7. Java8 Stream API

    Stream是Java8中,操作集合的一个重要特性. 从iteration到Stream操作 当你操作一个集合的时候,你通常的做法是迭代每一个元素,然后处理你想要的事情.举个例子: String co ...

  8. Java8 时间 API

    前言 Java8 中最为人津津乐道的新改变恐怕当属函数式 API 的加入.但实际上,Java8 所加入的新功能远不止这个. 本文将基于<Java SE8 for the Really Impat ...

  9. JAVA8 Stream API的使用

    /** * @auther hhh * @date 2018/12/31 12:48 * @description Stream流:用来处理数组.集合的API * 1.不是数据结构,没有内部存储(只是 ...

随机推荐

  1. 注重代码习惯,Python零基础从这本书籍开始!

    笨办法学 Python是Zed Shaw 编写的一本Python入门书籍.适合对计算机了解不多,没有学过编程,但对编程感兴趣的朋友学习使用.这本书以习题的方式引导读者一步一步学习编 程,从简单的打印一 ...

  2. The Involution Principle

    目录 Catalan Paths Vandermonde Determinant The Pfaffian Catalan Paths 从 \((0,0)\) 走到 \((n,n)\), 每次只能向上 ...

  3. fetch封装

    import {fetch as fetchPro} from "whatwg-fetch" import qs from "qs" const get=(ur ...

  4. C#LeetCode刷题之#860-柠檬水找零(Lemonade Change)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/4036 访问. 在柠檬水摊上,每一杯柠檬水的售价为 5 美元. 顾 ...

  5. C#LeetCode刷题之#53-最大子序和(Maximum Subarray)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/4012 访问. 给定一个整数数组 nums ,找到一个具有最大和的 ...

  6. Java多线程编程(6)--线程间通信(下)

      因为本文的内容大部分是以生产者/消费者模式来进行讲解和举例的,所以在开始学习本文介绍的几种线程间的通信方式之前,我们先来熟悉一下生产者/消费者模式.   在实际的软件开发过程中,经常会碰到如下场景 ...

  7. 靶机练习 - ATT&CK红队实战靶场 - 1. 环境搭建和漏洞利用

    最近某个公众号介绍了网上的一套环境,这个环境是多个Windows靶机组成的,涉及到内网渗透,正好Windows和内网渗透一直没怎么接触过,所以拿来学习下. 下载地址:http://vulnstack. ...

  8. 浏览器自动化的一些体会8 HttpWebRequest的几个问题

    前面说过了,httpWebRequest的好处在于轻量,不需要界面,缺点在于无法执行javascript.这里再归纳一些问题. 1. 设置代理 1) httpWebRequest不支持https的代理 ...

  9. PythonCrashCourse 第五章习题

    5.1编写一系列条件测试;将每个测试以及你对其结果的预测和实际结果都打印出来.你编写的代码应类似于下面这样: car = 'subaru' print("Is car == 'subaru' ...

  10. 送大家几个cnblogs号,供快捷评论,请谨慎使用

    欢迎评论& 3461896724@qq.com互动 可以在我的首页看更多 #1先送大家几个号:(密码都是 MLdlight2020)请区分大小写(可以直接复制) 写过一篇 免费验证码接收网站& ...