Stream API
Stream 是 数据渠道, 用于操作数据源(集合, 数组等) 所生成的元素序列。
"集合讲的是数据, 流讲的是计算!"
Stream 自己不会存储元素
Stream 不会改变源对象,相反,他们会返回一个持有结果的新Stream
Stream操作是延迟执行的, 这意味着他们会等到需要结果的时候才执行
操作步骤
创建Stream: 一个数据源 获取一个流
可以通过Collection 系列集合提供的stream() 串行流 或 parallelStream() 并行流
可以通过Arrays的静态方法 stream() 获取 数组流
可以通过Stream类中的静态方法 of()
创建无限流
中间操作: 对数据源的数据进行处理 filter() map() 等
终止操作: 一个终止操作,执行中间操作链,并产生结果
创建Stream
创建 Stream 示例:
@Testpublic 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); // 终止操作 }
@Testpublic 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--- 接收一个函数作为参数, 将 流中的每个值 都换成另一个流, 然后把所有流连续成一个流
提取员工姓名:
@Testpublic 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
@Testpublic 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);}// 定制排序 ,按照 年龄排 ,年龄相同 按照 姓名排, @Testpublic 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@ToStringpublic class Emp {private String name;private double salary;private int age;private Status status; //枚举类型// 空闲, 忙碌 ,休假public enum Status {FREE,BUSY,VOCATION }}//测试类@Testpublic 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()));// 改为minSystem.out.println(max.get());}
归约与收集
归约:
reduce(T t,BinaryOpeartor op) 或 reduce(BinaryOpeartor op) --可以将流中的元素 反复 结合起来, 得到一个值
@Testpublic 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 返回 DoubleDouble 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); // 没有初值, 返回OptionalSystem.out.println(reduce2.get());}
要 注意 reduce 有没有初值时 ,返回值 不同
收集
collect--- 将 流 转换为其他形式, 接收一个Collector接口的实现, 用于给 Stream中元素汇总的方法
Collector 接口中 方法的实现 决定了 如何对流执行 收集操作 (如 收集到 List Set Map)
Collectors 实现类提供了 很多静态方法, 可以方便地创建常见收集器实例
示例:
@Testpublic 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]
@Testpublic 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呢?
@Testpublic 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@ToStringpublic class Trader {private String name;private String city;}//交易类@AllArgsConstructor@NoArgsConstructor@Data@ToStringpublic class Transaction {private Trader trader;private int year;private int value;}// 交易测试类public class TestTrans {List<Transaction> transactions = null;@Beforepublic 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));}}根据以上内容 编写测试类, 完成以下练习题
@Testpublic 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());}