Java8 Stream列表去重 总结

今天遇到一个需求,需要对一个新闻列表根据标题进行去重,总结一下方法。

1,stream().distinct()

这个方法是Java8的Stream接口直接提供的方法,看起来最简单易用。
但是 distinct() 具体是使用对象的 hashCode() 和equals() 方法来判断列表中元素是否为同一个元素,所以必须重写对象的 hashCode 和 equals 方法,这就很麻烦了,而且之后如果元素如果有属性变动,可能还得修改,给自己埋坑。

Book对象重写hashCode() 和equals()
@Override
    public boolean equals(final Object obj) {
      if (obj == null) {
         return false;
      }
      final Book book = (Book) obj;
      if (this == book) {
         return true;
      } else {
         return (this.name.equals(book.name) && this.price == book.price);
      }
    }
    @Override
    public int hashCode() {
      int hashno = 7;
      hashno = 13 * hashno + (name == null ? 0 : name.hashCode());
      return hashno;
    }

实际调用
list.stream().distinct().forEach(b -> System.out.println(b.getName()+ "," + b.getPrice()));
特别说明:

distinct() 执行有状态的中间操作,在有序流的并行流的情况下,保持 distinct() 的稳定性是需要很高的代价的,因为它需要大量的缓冲开销。如果我们不需要保持遭遇顺序的一致性,那么我们应该可以使用通过 Stream.unordered() 使用无序流来提升性能。

lomok插件的影响:

项目中我发现自己没有重写Bean对象的 hashCode 和 equals 方法,使用 distinct 也可以得到正确的去重结果。仔细思索,发现是 lomok 插件的影响,我使用 @data 或 @EqualsAndHashCode 注解会自动为对象重写 hashCode equals 方法,可以去项目的/target/classes文件夹下找到该类。

2,自定义去重函数(推荐)

这也是网上的方法,自己写个工具函数进行去重,调用Stream的filter方法过滤数据。
简单好用,而且可定制性很强,key函数根据实际需要随便改动,不影响其他地方。
底层使用了 ConcurrentHashMap 进行边查边改,简单易懂。

去重工具函数:
static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        Map<Object,Boolean> seen = new ConcurrentHashMap<>();
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
调用过程:
list.stream().filter(distinctByKey(item -> item.getName())); 

猜你喜欢

转载自blog.csdn.net/iamcodingmylife/article/details/89241491
今日推荐