Java8新特性-Stream

发布时间:2024年01月12日

Stream API

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

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

Stream 自己不会存储元素

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

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

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

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

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

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

    创建无限流

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

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

创建Stream

创建 Stream 示例:

?
@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 完成了 内部迭代

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

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

@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);    // 终止操作
}
@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--- 接收一个函数作为参数, 将 流中的每个值 都换成另一个流, 然后把所有流连续成一个流

提取员工姓名:

?@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()?
? 在测试类中 编写如下代码 :
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?
? 在测试类 执行以下代码:
? 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

@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--- 内部迭代

?//为 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) --可以将流中的元素 反复 结合起来, 得到一个值

?@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 实现类提供了 很多静态方法, 可以方便地创建常见收集器实例

示例:

?@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());
?}

文章来源:https://blog.csdn.net/ly121862/article/details/135551509
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。