List去重的三种方法,以及性能比较

1.Collectors.collectingAndThen

  • 这是Stream中的一个收集器,相比普通的Collectors.toList、Collectors.groupingBy等收集器
  • Collectors.collectingAndThen还可以在收集之后进行某种操作
  • 多一个形参,用于写function函数(有入参有出参)

源码分析

举例说明collectingAndThen:

List按某属性去重,返回List

例1

2.本质流程

使用Collectors.collectingAndThen的本质是:先把new Set( list ) 获取一个Set,然后new List ( set )返回List

3. 如何用Set去重

用TreeSet和HashSet都能去重,只是去重的逻辑不一样。先看二者的存储逻辑:

  • Hashset是通过复写hashCode()方法和equals()方法来保证的。
  • 而Treeset是通过Compareable接口的compareto方法来保证的。同理可以用Comparator来定制排序

3.1TreeSet去重

①自然排序Comparable接口

@Data
public class DtoReq implements Comparable {

 private String userName;
 private String password;
 
 @Override
 public int compareTo(Object o) {
  DtoReq req =  (DtoReq)o;
  return req.getUserName().compareTo(this.getUserName());
 }
}
  • 代码逻辑即:对userName字段进行比较,且TreeSet的去重功能不需要关注顺序,谁比谁都一样
  • 不过这种方式相当于写死了,耦合度太高

去重代码

ArrayList<DtoReq> collect = arrayList.stream().collect(
    Collectors.collectingAndThen(
        Collectors.toCollection(
            TreeSet::new),ArrayList::new)
);

new TreeSet(arrayList)时构造器会自动去比较Comparable接口的compareTo方法,而达到去重的效果

②定制排序Comparator接口

  • 当元素的类型没实现java.lang.Comparable接口而又不方便修改代码,或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,那么可以考虑使用 Comparator 的对象来排序

  • 应该用匿名对象重写compare方法 或用lambda表达式调用Comparator.comparing方法

    @Data
    public class DtoReq {

    private String userName;
    private String password;

    }

匿名对象重写compare方法

/**
 * 定制排序Comparator对象,封装了排序的逻辑
 * 这里的泛型千万不能省,会用作lambda表达式的类型推断
 */
Comparator<DtoReq> comparator = new Comparator() {
  @Override
  public int compare(Object o1, Object o2) {
    DtoReq r1 = (DtoReq)o1;
    DtoReq r2 = (DtoReq)o2;
    return r1.getUserName().compareTo(r2.getUserName());
  }

};

ArrayList<DtoReq> collect = arrayList.stream().collect(
    Collectors.collectingAndThen(
        Collectors.toCollection(
            () -> new TreeSet<>(comparator)
        )
        , ArrayList::new)
);

静态方法comparing+lambda表达式

不需要new那个Comparator对象并重写其compare方法,直接调用静态方法Comparator.comparing方法,其形参是方法引用,意为:针对某属性进行排序,当然这里不是为了排序,只是为了去重

ArrayList<DtoReq> collect = arrayList.stream().collect(
    Collectors.collectingAndThen(
        Collectors.toCollection(
            () -> new TreeSet<>(Comparator.comparing(DtoReq::getUserName))
        )
        , ArrayList::new)
);

3.2HashSet去重

  • 利用HashSet去重,只需要重写其equals()hashCode()方法
  • 而好巧不巧lombok的@data注解提供了这两个方法的重写

lombok引入@data

@Data
public class DtoReq {

 private String userName;
 private String password;

}

此时就已经有了equals()和hashCode()方法,直接用就好

直接调HashSet构造器

ArrayList<DtoReq> collect = arrayList.stream().collect(
    Collectors.collectingAndThen(
        Collectors.toCollection(
            () -> new HashSet<>()
        )
        , ArrayList::new)
);

方法引用进行优化

ArrayList<DtoReq> collect = arrayList.stream().collect(
    Collectors.collectingAndThen(
        Collectors.toCollection(
            HashSet::new) , ArrayList::new)
);

4.去重逻辑

5.为什么不用distinct()

  • Stream流有一个distinct()方法可以去重,但是这个distinct()是直接调用当前流中存储对象的equals()方法
  • 用distinct()不能针对某个属性来去重
  • 即便是在可以使用distinct()的场景下, 也不建议使用

List去重以及效率分析
参考2

6.三种方式的去重效率分析

结论:HashSet>TreeSet>>distinct(),如果要保证去重后有序,就用TreeSet
,至于其他的去重方式,基本可以不考虑了

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值