Stream 是 数据渠道, 用于操作数据源(集合, 数组等) 所生成的元素序列。
"集合讲的是数据, 流讲的是计算!"
Stream 自己不会存储元素
Stream 不会改变源对象,相反,他们会返回一个持有结果的新Stream
Stream操作是延迟执行的, 这意味着他们会等到需要结果的时候才执行
创建Stream: 一个数据源 获取一个流
可以通过Collection 系列集合提供的stream() 串行流 或 parallelStream() 并行流
可以通过Arrays的静态方法 stream() 获取 数组流
可以通过Stream类中的静态方法 of()
创建无限流
中间操作: 对数据源的数据进行处理 filter() map() 等
终止操作: 一个终止操作,执行中间操作链,并产生结果
创建 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); ? } ??
给定一个数字列表, 如何返回一个由每个数的平方构成的列表呢?
给定[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()); ?}