分组:Stream不得不知的操作
场景
假如现在存在两个列表List 和List 。
其中A包含 B的集合。
代码如下。
A对象
@Datapublic void A{ private Integer id; private String name; private List bList;}
B 对象
@Datapublic void B{ private Integer AId; private String name; private String storeName; private Long startTime; private BigDecimal amount;...}
现在需要将符合条件的List的内容set到List中
用来组合出一个新的VO返回出去。
常规解决方案
先讲List循环,然后根据条件,找到对应的List的对象List,
一条条set到A中去。
常规伪代码
List listA;List listB;listA.foreach(a->{ List newListB = new ArrayList(); for(int i=0;i<listB.size();i++){ if(a.id==listB[i].AId){ newListB.add(listB[i]); } } a.setBList(newListB);});
我们可以看到,在常规操作里面,可以看到当前会有大量的循环。
循环影响性能,这个事情我们就不作过多讨论了。
而且代码看起来的话,大量的嵌套和判断,导致代码的自读性差。
Stream常规操作
如果熟悉stream的话,
一般都会把里面嵌套的那层for使用stream来操作。
例如
List listA;List listB;listA.foreach(a->{ List newListB = listB.stream.filter(e->e.getAId==a.getId).collect(Collectors.toList()); a.setBList(newListB); });
上面的stream操作也会产生很多的对象
同时,每次循环都会将listB全部比对一次。
在性能上来讲,还是有待提高。
那么有没有一种比较优雅的方案呢?
在这里,推荐一种新的方案。
新解决方案
Stream的groupingBy方法
使用这个方法的源码在Collectors中。
groupingBy源码
public final class Collectors { ... /* @param the type of the input elements * @param the type of the keys * @param classifier the classifier function mapping input elements to keys * @return a {@code Collector} implementing the group-by operation * * @see #groupingBy(Function, Collector) * @see #groupingBy(Function, Supplier, Collector) * @see #groupingByConcurrent(Function) */ public static Collector<T, ?, Map<K, List>> groupingBy(Function classifier) { return groupingBy(classifier, toList()); } ...}
通过groupingBy的分类器,可以直接将当前List中的数据,进行分类。
这样得到的数据就可以直接赋值了。
示例
我们还是拿原来的代码来操作
List listA;List listB;//跳过赋值操作Map<Integer,List> bMap = listB.stream.collect(Collectors.groupingBy(B::getAId));listA.foreach(a->{ a.setBList(bMap.get(a.getId())); });
我们将listB直接进行分组,使用map进行存储。map对应的key就是groupingBy中的分类器B::getAId。value就是对应的分组的数据。
这样一看,我们的代码非常简洁。
总结
groupingBy在分类操作的时候非常有用,我们可以直接将数据分好组。在后续需要调用的时候,直接get就好,就不需要进行二次计算操作了。在执行速度和代码可读性方面都有不错的提升。
顺便再上面的中间,再加点料,如果需要根据AId来对分组的amount属性求和。这样的话,你会怎样做呢?