目录
4.1 简单介绍
4.2 Stream的组成部分
4.2.1 数据源
4.2.2 中间操作
4.2.2.1 filter
4.2.2.2 map
4.2.2.3 distinct
4.2.2.4 sort
4.2.2.5 flatMap
4.2.3 终止操作
4.2.3.1 forEach
4.2.3.2 allMatch && anyMatch && noneMatch
4.2.3.3 findFirst和findAny 查找,
4.2.3.4 max、min和count,count()
4.2.3.5 reduce 归约(将Stream流中的元素转换成一个值)
4.2.3.6 collect 收集(将Stream流中的元素转换成一个容器)
4.2.3.6.1 接受三个参数
4.2.3.6.2 接受Collector接口
4.1 简单介绍
之前的lamdba和函数式接口就是为了Stream做准备
函数式接口 Function -->Stream中的map
函数式接口 Predicate -->Stream中的filter
Stream的使用 ,让我们的代码更加简洁,易懂(易维护)。它的使用,减少了大量的if条件语句和for循环语句,从输入到输出,像一条河流一样,让维护者读起来更像是读一篇文章。
4.2 Stream的组成部分
一个Stream流主要由三部分组成,即数据源、中间操作、终止操作。
注意点:
stream不存储值,通过管道获取值
不会改变原对象,相反,他们会返回一个持有结果的新stream。
stream操作是延迟执行的,这意味着他们会等到需要结果的时候才执行(过滤,映射,排序)
惰性求值(中间的操作):只有终止操作执行,中间操作才会被执行。xxxx().yyyy().zzzzz()
及早求值(终止操作)
4.2.1 数据源
常用的创建流方式
Stream.of,我们可以通过Stream的静态方法,传入一个泛型数组,或者多个参数,创建一个流。
Arrays.stream,我们可以通过Arrays的静态方法,传入一个泛型数组,创建一个流。
Collection.stream,可以用过集合的接口的默认方法,创建一个流;使用这个方法,包括继
Collection的接口,如:Set,List,Map等等。
通过文件生成Stream流
4.2.2 中间操作
常用的中间操作,以list.stream()数据源为例
有状态的中间操作要等到上面无状态的中间操作都已做完,在这里统一操作
4.2.2.1 filter
过滤流中符合条件的元素。经过list.stream().filter(item->item>0)的操作 ,流中只剩下大于0的item元素。
4.2.2.2 map
元素类型转化。Integer集合list,经过list.stream().map(item->item.toString())的操作 ,流中每个元素转化为了String类型。
4.2.2.3 distinct
流中元素去重。经过list.stream().distinct()操作,流中重复的元素被干掉。特别说明,distinct去重依据的是hashCode和equals方法,如果元素是对象,若要求按照对象的某属性去重需要重写 hashCode和equals方法。
//标签管理功能模块.允许用户批量添加标签,后台需要对标签去重,并且需要防止数据库中存在同名的标签tagListFromDB:数据库中存在的标签名列表{李四,王五,赵六}tagListFromReq:用户提交的{(张三,10),(李四,30),(张三,10)}//distinct:使用equa方法对引用对象进行比较//filter:最后得到的是为true的对象tagListFromReq.stream.filter(tag ->!tagListFromDB.containsKey(tag.getname)).distinct.foreach(tag -> system.out.println(tag))
4.2.2.4 sort
流中元素排序。经过list.stream().sort()操作,流中元素被按照自然排序。Student对象组成的list,若要求按照学生的年龄逆序,即list.stream().sorted(Comparator.comparing(Student::getAge).reversed()),使用sort排序的每个元素必须实现Comparable 接口。
//在股票中,撮合交易的原则是一段时间内的交易申请,价格越高的先成交;价格一样,下单时间最早的先成交;//价格和时间一致,交易量大的先成交;如果价格,时间和交易量和一致,机构优先成交,散户最后成交trades.stream().sorted(Comparator//优先按照价格进行排序(自然排序是从小大到),要有一个取反的操作.comparing(Trade::getPrice,Comparator.reverseOrder())//按照时间进行排序.thenComparing(Trade::getTime)//按照数量进行排序.thenComparing(Trade::getCount,Comparator.reverseOrder()).thenComparing(Trade::getType,(type1,type2) -> {if("机构".equals(type1) && "散户".equals(type2)){return -1;}else if("机构".equals(type2) && "散户".equals(type1)){return 1;}else{return 0;}}))
4.2.2.5 flatMap
流的扁平化,即降维合并处理。一个List>流list,经过list.stream().flatMap(item->item.stream())的处理,变为List流。
//问题:stream --stream?????flatMap(strings -> Arrays.stream(strings))flatMap(Arrays::stream )//flatmap应用场景://需求:输出:hellozhangsan,hilisi......List list1 = Arrays.asList("hello", "hi", "你好");List list2 = Arrays.asList("zhangsan", "lisi", "wangwu");list1.stream().flatMap(item1 ->list2.stream().map(item2 ->item1+":"+item2)).collect(Collectors.toList()).forEach(System.out::println);
4.2.3 终止操作
常用的终止操作,以list.stream().filter(item->item!=null)中间操作为例
4.2.3.1 forEach
内部迭代。
4.2.3.2 allMatch && anyMatch && noneMatch
返回Boolean类型,allMatch—检查是否匹配所有元素,anyMatch—检查是否匹配任意元素,noneMatch—检查是否没有匹配所有元素。list.stream().filter(item->item!=null).allMatch(item->item>0)若流中元素全部大于0的返回true,否则返回false。
如果第一个元素符合>1000就输出第一个元素的name.如果第二个元素不符合>1000不再往下执行 如果第一个元素符合>1000就输出第一个元素的name不再往下执行
查找出有缺考记录的学生的姓名 ,Map集合自带的foreach中是(key,value)的形式
//班级中有20名学生,每名学生有5门课的考试成绩.其中缺考的科目分数字段为空,需要找出有缺考的学//生都叫什么名字//Map> studentMap :Map<学生名字,<学生的英语,数学,英语的成绩>>studentMap.foreach( (studentName,sutdentScoreList) ->{//anyMatch:是否有任意一个元素符合表达式,如果有直接返回true,不会往下继续执行 boolean bool = sutdentScoreList.stream.anyMatch(score -> { return score.getScorevalue() == null if(bool){ system.out.println(studentName); } });});
4.2.3.3 findFirst和findAny 查找,
返回Optional类型。list.stream().filter(item->item!=null).findFirst()得到T的Optional,若不为null,使用get()方法获取T;或者orElse(null)获取。
Stream stream = Stream.of(); Integer result = stream.findFirst().orElse(0); System.out.println(result);
4.2.3.4 max、min和count,count()
max()和min()接收一个Comparator接口参数,返回最大和最小的Optional。 Stream.of(1, 2,3,4,5).max(Integer::compareTo)获取元素最大的Optional,再使用get()可以得到5。 补充:当max有多条记录时,取流的第1条
4.2.3.5 reduce 归约(将Stream流中的元素转换成一个值)
图形化解释上面三个值
接收一个参数:Stream.of(1,2,3,4,5).reduce((a,b)->a+b),结果类型为Optional,调用get()方法得到15。
接收两个参数:Stream.of(1, 2, 3, 4, 5).reduce(5, (a, b) -> a + b),结果类型为Integer,值为20,第一个参数为初始值 。
接收三个参数:Stream.of(1, 2, 3, 4, 5).parallel().reduce(0, (a, b) -> a + b, (s1, s2) -> s1 + s2),第三个参数只有在并行流中才会执行 ,作用是将并行每个流的运算结果按照第三个参数的运算规则进行最终合并。
计算商品数量和商品的总金额
list.stream().reduce(//初始值new order(0,0,0.0),//stream中两个元素计算的逻辑(order1,order2)->{int count = order1.getCount+order2.getCount,int sum = order1.getSum+order2.getSum,return new order(0,count,sum)}//并行情况下,多个并行结果如何合并(order1,order2)->{int count = order1.getCount+order2.getCount,int sum = order1.getSum+order2.getSum,return new order(0,count,sum)})
4.2.3.6 collect 收集(将Stream流中的元素转换成一个容器) 4.2.3.6.1 接受三个参数
//最基本的一个方法 R collect(//结果容器 Supplier supplier, //将元素累加到结果容器中 BiConsumer accumulator, //合并结果容器 BiConsumer combiner);//Map<用户账号,订单(数量和金额)>public class StreamTest2 { public static void main(String[] args) { List list = new ArrayList(){{ add(new Order(1001,1)); add(new Order(1002,2)); add(new Order(1001,1)); add(new Order(1003,3)); add(new Order(1004,4)); add(new Order(1005,5)); add(new Order(1006,6)); }}; HashMap result = list.stream().collect( //初始化结果容器 () -> new HashMap(), //将元素添加到结果容器中 (HashMap map, Order item) -> { long account = item.getAccount(); if (map.containsKey(account)) { Order order = map.get(account); order.setCount(order.getCount()+item.getCount()); }else{ map.put(account,item); } }, //采用并行的方式:并行合并方式 (HashMap map1,HashMap map2) ->{ map2.forEach( (key,value) -> { map1.merge(key,value, (order1,order2) -> { return new Order(key,order1.getCount() + order2.getCount()); }); }); }); System.out.println(result); }}public class StreamTest2 { public static void main(String[] args) { List list = new ArrayList(){{ add(new Order(1001,1)); add(new Order(1002,2)); add(new Order(1001,1)); add(new Order(1003,3)); add(new Order(1004,4)); add(new Order(1005,5)); add(new Order(1006,6)); }}; HashMap result = list.stream().collect( //初始化结果容器 () -> { System.out.println("初始化结果容器"); return new HashMap();}, //将元素添加到结果容器中 (HashMap map, Order item) -> { System.out.println("将元素添加到结果容器中"); long account = item.getAccount(); if (map.containsKey(account)) { Order order = map.get(account); order.setCount(order.getCount()+item.getCount()); }else{ map.put(account,item); } }, //如果采用并行流合并中间结果 (HashMap map1,HashMap map2) ->{ System.out.println("采用并行流合并中间结果"); map2.forEach( (key,value) -> { //如果map1中发现了key,把map1中的value和value作为一个参数传递给 order1,order2:可以自定义处理逻辑 //如果key没有在map1中出现,就把key和value加到map1中 //最后返回的是map1 map1.merge(key,value, (order1,order2) -> { return new Order(key,order1.getCount() + order2.getCount()); }); }); }); result.forEach((account,order) -> { System.out.println("account"+account+",order:"+order); }); }}//打印结果//采用的不是并行流,只会初始化结果容器结果一次,不断的将7个元素添加到容器中(7次添加结果)//采用并行流HashMap result = list.stream().parallel().collect(......)//初始化结果容器7次//将元素分别添加到结果容器中7次//采用并行流合并中间结果6次
4.2.3.6.2 接受Collector接口
收集为List:list.stream().filter(item->item!=null).collect(Collectors.toList())
收集为Set:list.stream().filter(item->item!=null).collect(Collectors.toSet())
收集为HashSet:list.stream().filter(item->item!=null).collect(Collectors.toCollection(HashSet::new))
partitioningBy:
接受一个Predicate 返回类型:map> ;true满足条件的部分;false:不满足条件的部分
接受一个参数&&接受两个参数同groupingBy
public static void main(String[] args) { List list = new ArrayList(){{ add(new Order(1001,21)); add(new Order(1002,22)); add(new Order(1003,23)); add(new Order(1001,21)); add(new Order(1002,22)); add(new Order(1003,23)); add(new Order(1001,22)); add(new Order(1002,25)); add(new Order(1003,26)); }};Map>> result = list.stream().collect( Collectors.partitioningBy (item -> item.getAccount() == 1001, Collectors.partitioningBy( order -> order.getCount() > 21))); }
收集为Map
接收两个参数:
public static void main(String[] args) { List list = new ArrayList(){{ add(new Order(1001,1)); add(new Order(1002,2)); add(new Order(1003,3)); add(new Order(1004,4)); add(new Order(1005,5)); add(new Order(1006,6)); }};//第一个参数设置map的key,第二个参数设置map的value//如果Order::getAccount(key)有重复值会抛出异常:java.lang.IllegalStateException:Duplicate key Map result1 = list.stream().collect(Collectors.toMap(Order::getAccount, Function.idenity()));}
接受三个参数:
public static void main(String[] args) { List list = new ArrayList(){{ add(new Order(1001,1)); add(new Order(1001,1)); add(new Order(1002,2)); add(new Order(1003,3)); add(new Order(1004,4)); add(new Order(1005,5)); add(new Order(1006,6)); }};Map result2 = list.stream().collect(Collectors.toMap( Order::getAccount, Function.identity(), //前两个参数同上 //出现相同的key时的处理逻辑 (a, b) -> { a.setCount(a.getCount()+b.getCount()); return a;} result2.forEach( (key,value) -> System.out.println(key+":"+value)); }
接收四个参数:
public static void main(String[] args) { List list = new ArrayList(){{ add(new Order(1001,1)); add(new Order(1001,1)); add(new Order(1002,2)); add(new Order(1003,3)); add(new Order(1004,4)); add(new Order(1005,5)); add(new Order(1006,6)); }};linkedHashMap result2 = list.stream().collect(Collectors.toMap( Order::getAccount, Function.identity(), (a, b) -> { a.setCount(a.getCount()+b.getCount()); return a;}, //前三个参数同上,第四个参数表示设置接收map的容器为linkedHashMap linkedHashMap::new)); result2.forEach( (key,value) -> System.out.println(key+":"+value)); }
分组groupingBy
接收一个参数:按照key分组,得到Map>类型结果。
@see #groupingByConcurrent(Function) //需求:按照账户分组(根据什么来分组什么就是key)public static void main(String[] args) { List list = new ArrayList(){{ add(new Order(1001,21)); add(new Order(1001,21)); add(new Order(1002,22)); add(new Order(1002,22)); add(new Order(1003,23)); }}; Map> result = list.stream().collect(Collectors.groupingBy(Order::getAccount)); result.forEach((key,value) -> System.out.println(key+":"+value)); }
接收两个参数:第二个参数重新定义了map的value收集类型。
public static void main(String[] args) { List list = new ArrayList(){{ add(new Order(1001,21)); add(new Order(1002,22)); add(new Order(1003,23)); add(new Order(1001,21)); add(new Order(1002,22)); add(new Order(1003,23)); add(new Order(1001,22)); add(new Order(1002,25)); add(new Order(1003,26)); }};//按照账户分组(根据什么来分组什么就是key),在上一次分组内再根据数量分组Map>> result = list.stream().collect(Collectors.groupingBy(Order::getAccount,Collectors.groupingBy(Order::getCount))); result.forEach((key,value) -> System.out.println(key+":"+value)); }//按照账户分组(根据什么来分组什么就是key),并且统计分组内的数据Map result = list.stream().collect(Collectors.groupingBy(Order::getAccount,Collectors.counting())); result.forEach((key,value) -> System.out.println(key+":"+value));public static void main(String[] args) { List list = new ArrayList(){{ add(new Order(1001,21)); add(new Order(1002,22)); add(new Order(1003,23)); add(new Order(1001,21)); add(new Order(1002,22)); add(new Order(1003,23)); add(new Order(1001,22)); add(new Order(1002,25)); add(new Order(1003,26)); }};list.stream().collect( Collectors. groupingBy( Order::getAccount,Collectors.collectingAndThen( //minby的返回值: Optional Collectors.minBy(Comparator.comparingInt(Order::getCount)),item -> item.get()))) .forEach( (key,value) -> System.out.println(key+":"+value));}
接收三个参数:其他两个参数同上,第三个参数是提供一个新的结果容器
public static void main(String[] args) { List list = new ArrayList(){{ add(new Order(1001,21)); add(new Order(1002,22)); add(new Order(1003,23)); add(new Order(1001,21)); add(new Order(1002,22)); add(new Order(1003,23)); add(new Order(1001,22)); add(new Order(1002,25)); add(new Order(1003,26)); }}; HashMap result1 = list.stream().collect( //提供一个新的结果容器:() -> new HashMap<>() Collectors.groupingBy(Order::getAccount, () -> new HashMap<>(), Collectors.counting())); result1.forEach((key,value) -> System.out.println(key+":"+value)); HashMap>> result2 = list.stream().collect( //提供一个新的结果容器:() -> new HashMap<>()Collectors.groupingBy(Order::getAccount,HashMap::new,Collectors.groupingBy(Order::getCount)));result2.forEach((key,value) -> System.out.println(key+":"+value)); System.out.println(result2.getClass());}
综合案例:
//设计一个对外提供服务的接口,支持调用方传入多个账户编号查询订单并且对账户编号进行分组,区分出哪个订单//属于哪个账户的Order:有两个属性:账户编号,订单号public Map> query(list accountIds){ return Optional.ofNullable(selectFromDB(accountIds)).map(List::Stream) .orElseGet(Streram::empty) .collect(Collectors.groupingBy(order -> order.getAccountId))}