Java8新特性-Stream

Stream API

Stream 是 数据渠道, 用于操作数据源(集合, 数组等) 所生成的元素序列。

"集合讲的是数据, 流讲的是计算!"

Stream 自己不会存储元素

Stream 不会改变源对象,相反,他们会返回一个持有结果的新Stream

Stream操作是延迟执行的, 这意味着他们会等到需要结果的时候才执行

操作步骤
  1. 创建Stream: 一个数据源 获取一个流

    可以通过Collection 系列集合提供的stream() 串行流 或 parallelStream() 并行流

    可以通过Arrays的静态方法 stream() 获取 数组流

    可以通过Stream类中的静态方法 of()

    创建无限流

  2. 中间操作: 对数据源的数据进行处理 filter() map() 等

  3. 终止操作: 一个终止操作,执行中间操作链,并产生结果

创建Stream

创建 Stream 示例:

复制代码
 
java 复制代码
@Test
 public void test(){
 //1. 可以通过Collection 系列集合提供的stream() 串行流 或 parallelStream() 并行流
  List<String> list = new ArrayList<>();
  Stream<String> s1 = list.stream();
 ​
  //2.可以通过Arrays的静态方法 stream() 获取 数组流
  Integer[] s = {2,3,4,5};
  Stream<Integer> s2 = Arrays.stream(s);
 ​
  //3.可以通过Stream类中的静态方法 of()
  Stream<String> ss = Stream.of("abc","123");
 ​
  //4.创建无限流
 //迭代
      Stream<Integer> sss = Stream.iterate(0,x->x+2);
 ​
      sss.limit(4).forEach(System.out::println);
 ​
      //生成
      Stream.generate(()->Math.random()).limit(4).forEach(System.out::println);
 ​
 }
中间操作

中间操作

多个中间操作 可以连起来形成一个流水线 , 除非流水线上触发终止操作,否则 中间操作不会执行任何的处理

而在 终止操作时一次性全部处理,成为 "惰性求值"

筛选与切片

filter----- 接收lambda, 从流中排除某些元素

limit(n)------ 截断流, 使其元素不超过给定数量n

skip(n)------- 跳过元素,返回一个扔掉了前n个元素的流, 若流中元素不足n个,则返回一个空流, 与 limit(n) 互补

distinct() --筛选, 通过流所生成元素的hashCode()和equals() 去除 重复元素
虽然代码中没有进行迭代调用,但 Stream API 完成了 内部迭代

中间操作不会执行任何的处理

而在 终止操作时一次性全部处理,成为 "惰性求值"

java 复制代码
@Test
public void test(){
List<Emp> emps = Arrays.asList(
        new Emp("王丽丽",4000,50),
        new Emp("李四",5000,40),
        new Emp("王丽丽2",6000,50),
        new Emp("刘莉莉",7000,60)
        );
// 筛选  薪资大于5000的员工信息
emps.stream()                                 //创建流 
            .filter(x->x.getSalary()>5000)    //中间操作
            .forEach(System.out::println);    // 终止操作
}
java 复制代码
@Test
 public void test(){
    List<Emp> emps = Arrays.asList(
            new Emp("王丽丽",4000,50),
            new Emp("李四",5000,40),
            new Emp("王丽丽2",6000,50),
            new Emp("刘莉莉",7000,60)
            );
    // 筛选  薪资大于5000的员工信息
    emps.stream()
                .filter(x->x.getSalary()>4000)
                .limit(2)
                .forEach(System.out::println);
 }
复制代码
如果 filter 筛选后 有 5 条 符合条件的, 那么 limit(2) 就是取 前2条
                                那么 skip(2) 就是 跳过前2条 ,往后取2条
 distnict() 	去重 要注意 实体类 重写 hashCode与equals 方法
映射

map()---- 接收 Lambda ,将 元素转换成其他形式提取信息, 接收一个函数作为参数, 该函数会被应用到每个元素上,并将其映射成一个新的元素

flatMap--- 接收一个函数作为参数, 将 流中的每个值 都换成另一个流, 然后把所有流连续成一个流
提取员工姓名:

java 复制代码
 @Test
 public void test(){
 List<Emp> emps = Arrays.asList(
         new Emp("王丽丽",4000,50),
         new Emp("李四",5000,40),
         new Emp("王丽丽2",6000,50),
         new Emp("刘莉莉",7000,60)
         );
 // 提取员工姓名
 emps.stream()
         .map(e->e.getName())    // 或 .map(Emp::getName)
         .forEach(System.out::println);
 }
 ​
复制代码
​
 map 与flatMap 关系 类似于 List 接口中 add() 与addAll()​
  在测试类中 编写如下代码 :
java 复制代码
List<String> list1  =Arrays.asList("aaa","bbbb","ccc");
     List list2 = new ArrayList();
     list2.add(2222);
     list2.add(111);
     list2.add(list1);
      System.out.println(list2);
复制代码
 //此时  list2 为 [2222, 111, [aaa, bbbb, ccc]]    , 长度为 3​
  在测试类 执行以下代码:
java 复制代码
  List<String> list1  =Arrays.asList("aaa","bbbb","ccc");
     List list2 = new ArrayList();
     list2.add(2222);
     list2.add(111);
     list2.addAll(list1);
      System.out.println(list2);
      System.out.println(list2.size());
复制代码
 ​
 //此时  list2 为 [2222, 111, aaa, bbbb, ccc] , 长度 为 5 , ​  ​
排序

sorted()----- 自然排序 --使用 Comparable

sorted(Comparator com)----- 定制排序-- 使用 Comparator

java 复制代码
@Test
 public void test(){
 List<Emp> emps = Arrays.asList(
         new Emp("王丽丽",4000,50),
         new Emp("李四",5000,40),
         new Emp("王丽丽2",6000,50),
         new Emp("刘莉莉",7000,60)
         );
 // 提取员工薪资 并 升序 排列
 emps.stream()
         .map(Emp::getSalary)
         .sorted()
         .forEach(System.out::println);
 ​
 }
 ​
 ​
 // 定制排序 ,按照 年龄排 ,年龄相同 按照 姓名排, 
 @Test
  public void test(){
     List<Emp> emps = Arrays.asList(
             new Emp("王丽丽",4000,50),
             new Emp("李四",5000,40),
             new Emp("王丽丽2",6000,50),
             new Emp("刘莉莉",7000,60)
             );
     // 提取员工姓名
     emps.stream()
             .sorted((e1,e2)->{
                 if(e1.getAge() == e2.getAge()){
                     return e1.getName().compareTo(e2.getName());
                 }else{
                     return Integer.compare(e1.getAge(),e2.getAge()); // 升序, 可以返回负的 就是降序
                 }
             }).forEach(System.out::println);
 ​
  }
 ​
终止操作

终端操作 会从 流的流水线 生成结果, 其结果可以是任何不是流的值

查找与匹配

allMatch------ 检查是否匹配所有元素 ,返回值为 boolean

anyMatch-----检查是否至少匹配一个元素 返回值为 boolean

noneMatch---- 检查是否没有匹配所有元素 返回值为 boolean

findFirst---- 返回第一个元素, 返回Optional 对象, 可以通过get() 获得首个元素

findAny----- 返回当前流中的任意元素,返回Optional 对象, 可以通过get() 获得首个元素

count-----返回流中元素的总个数 , 返回long

max------ 返回流中最大值

min------返回流中最小值

forEach--- 内部迭代

java 复制代码
 //为 Emp.java 增加枚举类型 Status
 @NoArgsConstructor
 @AllArgsConstructor
 @Data
 @ToString
 public class Emp {
     private String name;
     private double salary;
     private int age;
     private Status status;  //枚举类型
 ​
     //  空闲, 忙碌 ,休假
     public enum Status {
         FREE,BUSY,VOCATION   
     }
 }
 ​
 //测试类
 @Test
     public void test(){
        List<Emp> emps = Arrays.asList(
                new Emp("王丽丽",4000,50, Emp.Status.FREE),
                new Emp("李四",5000,40, Emp.Status.FREE),
                new Emp("王丽丽2",6000,50,Emp.Status.FREE),
                new Emp("刘莉莉",7000,60,Emp.Status.FREE)
                );
        //检测 员工是否都属于 BUSY 状态
         boolean b = emps.stream()
                 .noneMatch(e -> e.getStatus().equals(Emp.Status.FREE));
 ​
         System.out.println(b);
         // 取 工资最高的 员工信息
         Optional<Emp> first = emps.stream()
                 .sorted((e1, e2) -> -Double.compare(e1.getSalary(), e2.getSalary()))
                 .findFirst();
         System.out.println(first.get());  //获得 员工的相关信息
 ​
         
          // stream() 串行流, parallelStream() 并行流
         Optional<Emp> first = emps.parallelStream()
                 .filter(e->e.getStatus().equals(Emp.Status.BUSY))
                 .findAny();
         System.out.println(first.get());
         
         
         
         // 取 工资最高(最低)的 员工信息
         Optional<Emp> max = emps.stream()
 ​
                .max((e1,e2)->Double.compare(e1.getSalary(),e2.getSalary()));// 改为min
 ​
         System.out.println(max.get());
     }
 ​
 ​
归约与收集

归约:

reduce(T t,BinaryOpeartor op) 或 reduce(BinaryOpeartor op) --可以将流中的元素 反复 结合起来, 得到一个值

java 复制代码
 @Test
 public void test(){
 ​
 List<Integer> nums= Arrays.asList(1,2,3,4,5,6,7,8,9,10);
 // 对集合里的数 进行累加求和
  Integer reduce = nums.stream()
          .reduce(0, (x, y) -> x + y); // 0 是 初始值, 第二个参数 要执行累加操作
  System.out.println("sum="+reduce);  //55
 ​
 ​
  // 计算当前 所有员工的 薪资总和
  List<Emp> emps = Arrays.asList(
              new Emp("王丽丽",4000,50, Emp.Status.BUSY),
              new Emp("李四",5000,40, Emp.Status.FREE),
              new Emp("王丽丽2",6000,50,Emp.Status.BUSY),
              new Emp("王丽丽3",9000,50,Emp.Status.BUSY),
              new Emp("王丽丽4",1000,50,Emp.Status.BUSY)
 ​
      );
  //  有 初值0.0 返回 Double
      Double reduce1 = emps.stream()
              .map(e -> e.getSalary())    //提取薪资
              .reduce(0.0, (e1, e2) -> e1 + e2); //薪资求和  Double::sum 也可以
      System.out.println(reduce1);
 ​
 ​
      // 没有初值 返回 Optional 对象
       Optional<Double> reduce2 = emps.stream()
              .map(e -> e.getSalary())    //提取薪资
              .reduce(Double::sum);  // 没有初值, 返回Optional
 ​
      System.out.println(reduce2.get());
 }
复制代码
要 注意 reduce 有没有初值时 ,返回值 不同 

收集

collect--- 将 流 转换为其他形式, 接收一个Collector接口的实现, 用于给 Stream中元素汇总的方法

Collector 接口中 方法的实现 决定了 如何对流执行 收集操作 (如 收集到 List Set Map)

Collectors 实现类提供了 很多静态方法, 可以方便地创建常见收集器实例

示例:

java 复制代码
 @Test
  public void test(){
 ​
      List<Emp> emps = Arrays.asList(
              new Emp("王丽丽",4000,50, Emp.Status.BUSY),
              new Emp("李四",5000,40, Emp.Status.FREE),
              new Emp("王丽丽2",6000,50,Emp.Status.BUSY),
              new Emp("王丽丽3",9000,50,Emp.Status.BUSY),
              new Emp("王丽丽4",1000,50,Emp.Status.BUSY)
 ​
      );
     // 收集 所有员工的姓名,并放到集合中去
      List<String> collect = emps.stream()
              .map(Emp::getName)
              .collect(Collectors.toList());
      collect.forEach(System.out::println);
 ​
       // 总工资的平均值   averagingDouble 平均值, summingDouble 求和,maxBy 最大, minBy 最小
      Double avg = emps.stream()
              .collect(Collectors.averagingDouble(Emp::getSalary));
      System.out.println(avg);
 ​
       // 分组, 计算 每个状态的 薪资总和
      Map<Emp.Status, List<Emp>> group = emps.stream()
              .collect(Collectors.groupingBy(Emp::getStatus));
      System.out.println(group);
 ​
      // 分区, 满足条件一个区, 不满足条件的一个区
      // 薪资大于等于6000 为一区, 小于为 另一区
      Map<Boolean, List<Emp>> collect = emps.stream()
              .collect(Collectors.partitioningBy(e -> e.getSalary() >= 6000));
      System.out.println(collect);
 ​
      // 提取姓名, 并用 逗号 分隔, 首尾的逗号 均已去除
      String str = emps.stream().map(Emp::getName).collect(Collectors.joining(","));
      System.out.println(str);
  }
 ​

Stream Api 练习题

给定一个数字列表, 如何返回一个由每个数的平方构成的列表呢?

给定[1,2,3,4,5], 应该返回[1,4,9,16,25]

复制代码
 @Test
 public void test4(){
     List<Integer> list = Arrays.asList(1,2,3,4,5);
     list.stream()
             .map(e->e*e)
             .forEach(System.out::println);
 }

怎样用 map 和reduce 方法 数一数 流中 有多少个Emp呢?

复制代码
 @Test
 public void test4(){
     List<Emp> emps = Arrays.asList(
             new Emp("王丽丽",4000,50, Emp.Status.BUSY),
             new Emp("李四",5000,40, Emp.Status.FREE),
             new Emp("王丽丽22",8000,50,Emp.Status.VOCATION),
             new Emp("王丽丽4",1000,50,Emp.Status.VOCATION)
 ​
     );
 ​
     Integer reduce = emps.stream()
             .map(e -> 1)
             .reduce(0, (e1, e2) -> e1 + e2);  //.reduce(0,Integer::sum);
     System.out.println(reduce);
 }
复制代码
 //交易员类
 @AllArgsConstructor
 @NoArgsConstructor
 @Data
 @ToString
 public class Trader {
 private String name;
 private String city;
 }
复制代码
 //交易类
 @AllArgsConstructor
 @NoArgsConstructor
 @Data
 @ToString
 public class Transaction {
 private Trader trader;
 private int year;
 private int value;
 ​
 }
复制代码
 // 交易测试类
 public class TestTrans {
 ​
 List<Transaction> transactions = null;
 ​
 @Before
 public void before(){
   Trader t1 = new Trader("t1","tj");
   Trader t2 = new Trader("t2","sh");
   Trader t3 = new Trader("t3","tj");
   Trader t4 = new Trader("t4","tj");
 ​
   transactions = Arrays.asList(
           new Transaction(t4,2011,300),
           new Transaction(t1,2012,1000),
           new Transaction(t1,2011,400),
           new Transaction(t2,2012,710),
           new Transaction(t2,2012,700),
           new Transaction(t3,2012,950)
   );
 }
 }

根据以上内容 编写测试类, 完成以下练习题

复制代码
 @Test
 public void test(){
  //找出2011年发生的所有交易, 并按交易额排序, (从低到高)
  transactions.stream()
          .filter(x->x.getYear()==2011)
          .sorted((x1,x2)->Integer.compare(x1.getValue(),x2.getValue()))
          .forEach(System.out::println);
  //交易员都在哪些不同的城市工作过,
  transactions.stream()
          .map(e->e.getTrader().getCity())
          .distinct()
          .forEach(System.out::println);
  //查找所有来自 tj的交易员,并按姓名排序
  System.out.println("---------------3---------");
  transactions.stream()
          .filter(x->x.getTrader().getCity().equals("tj"))
          .map(x->x.getTrader().getName())
          .distinct()
          .sorted((x1,x2)->x1.compareTo(x2))
          .forEach(System.out::println);
  //返回所有交易员的姓名字符串,按字母顺序排序
  System.out.println("---------------4---------");
  transactions.stream()
          .map(x->x.getTrader().getName())
          .distinct()
          .sorted((x1,x2)->x1.compareTo(x2))
          .forEach(System.out::println);
  //有没有交易员是在sh工作的?
  boolean sh = transactions.stream()
          .anyMatch(x -> x.getTrader().getCity().equals("sh"));
  System.out.println("------="+sh);
  //打印生活在tj的交易员的所有交易额
  System.out.println("---6---");
  Integer sum = transactions.stream()
          .filter(x -> x.getTrader().getCity().equals("tj"))
          .map(x -> x.getValue())
          .reduce(0, (x1, x2) -> x1 + x2);
  System.out.println(sum);
 ​
  //所有交易中, 最高的交易额是多少
  Optional<Integer> max = transactions.stream()
          .map(x -> x.getValue())
          .max(Integer::compare);
  System.out.println("-------"+max.get());
  //找到交易额最小的交易
  Optional<Integer> min = transactions.stream()
          .map(x -> x.getValue())
          .min(Integer::compare);
  System.out.println("-------"+min.get());
 }
相关推荐
闲晨几秒前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
老猿讲编程27 分钟前
一个例子来说明Ada语言的实时性支持
开发语言·ada
Chrikk1 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*1 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue1 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man1 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang
测开小菜鸟2 小时前
使用python向钉钉群聊发送消息
java·python·钉钉
P.H. Infinity2 小时前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天3 小时前
java的threadlocal为何内存泄漏
java
caridle3 小时前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express