Stream按对象某属性去重的方案

2年前 (2022) 程序员胖胖胖虎阿
255 0 0

1.Collectors.collectingAndThen

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

Stream按对象某属性去重的方案

举例说明collectingAndThen:

List按某属性去重,返回List

Stream按对象某属性去重的方案

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.去重逻辑

Stream按对象某属性去重的方案

5.为什么不用distinct()

Stream流有一个distinct()方法可以去重,但是这个distinct()是直接调用当前流中存储对象的equals()方法,而Set会计算哈希值
List去重以及效率分析

版权声明:程序员胖胖胖虎阿 发表于 2022年9月21日 上午5:00。
转载请注明:Stream按对象某属性去重的方案 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...