分组:Stream不得不知的操作

Posted 沛沛老爹

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分组:Stream不得不知的操作相关的知识,希望对你有一定的参考价值。

场景

假如现在存在两个列表List<A> 和List<B> 。

其中A包含 B的集合。

代码如下。

A对象

@Data
public void A
    
    private Integer id;

    private String name;
    
    private List<B> bList;

B 对象

@Data
public void B
    private Integer AId;

    private String name;
    
    private String storeName;

    private Long startTime;
    
    private BigDecimal amount;

...

现在需要将符合条件的List<B>的内容set到List<A>中

用来组合出一个新的VO返回出去。

 

常规解决方案

一般解决方法都是直接使用for循环操作。

先讲List<A>循环,然后根据条件,找到对应的List<B>的对象List,

一条条set到A中去。

常规伪代码

List<A> listA;
List<B> listB;
listA.foreach(a->
    List<B> 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<A> listA;
List<B> listB;
listA.foreach(a->
    
    List<B> 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 <T> the type of the input elements
     * @param <K> 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 <T, K> Collector<T, ?, Map<K, List<T>>>
    groupingBy(Function<? super T, ? extends K> classifier) 
        return groupingBy(classifier, toList());
    
 ...

通过groupingBy的分类器,可以直接将当前List<B>中的数据,进行分类。

这样得到的数据就可以直接赋值了。

示例

我们还是拿原来的代码来操作


List<A> listA;
List<B> listB;
//跳过赋值操作
Map<Integer,List<B>> 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属性求和。这样的话,你会怎样做呢?

以上是关于分组:Stream不得不知的操作的主要内容,如果未能解决你的问题,请参考以下文章

JDK8对List进行分组操作(stream的groupby)

七件你不得不知的Linux小技巧

不得不知的排序六:堆排序

不得不知的排序二:直接插入排序

不得不知的排序八:基数排序

归约分组与分区,深入讲解JavaStream终结操作