java8流计算去重方法进阶版(优质文章)

这里一共介绍3种方式排序

1、Stream提供的distinct()方法只能去除重复的对象,无法根据指定的对象属性进行去重,可以应付简单场景。

2、


 
  1. List<Book> unique = books.stream().collect(

  2. collectingAndThen(toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getId()))),

  3. ArrayList::new));

使用上述代码可以根据指定元素去重(book的id属性),原理是把id作为key放入set中,然后在转换为list。这种方式能够去重,但不优雅,因为需要执行流的终端操作,把流转换为List。这样的话,流就不能继续使用了,不优雅,不好。但网上大多是这种方法。所以第三种方法才是我们要推荐的。

3、


 
  1. unique = books.stream()

  2. .filter(distinctByKey(o -> o.getId()))

  3. .collect(Collectors.toList());

这种方法是不是看着优雅多了?非常的java8了?是的。但这个distinctByKey()这个方法是自定义的。定义如下:


 
  1. public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {

  2. Map<Object, Boolean> seen = new ConcurrentHashMap<>();

  3. return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;

  4. }

非常感谢《Solr权威指南》的作者,兰小伟提供的代码和思路。这种方法要比网上绝大多数的方法好。

我来稍微描述一下这个方法,便于新手理解。

首先是filter方法,返回一个流,需要一个Predicate类型的参数(多嘴介绍下Predicate这个函数式接口,接受一个参数,返回布尔值)。

Stream<T> filter(Predicate<? super T> predicate)

filter根据Predicate返回的布尔值来判断是否要过滤掉,会过滤掉返回值为false的数据。而我们自己定义的distinctByKey返回值就是Predicate,所以可以作为参数传入filter。distinctByKey也需要一个Function的参数。distinctByKey先是定义了一个线程安全的Map(相比于Hashtable以及Collections.synchronizedMap(),ConcurrentHashMap在线程安全的基础上提供了更好的写并发能力,但同时降低了对读一致性的要求),因为在流计算中是多线程处理的,需要线程安全。然后将值作为key,TRUE作为value put到map中。这里的put方法使用的是putIfAbsent()。putIfAbsent()方法是如果key不存在则put如map中,并返回null。若key存在,则直接返回key所对应的value值。文章末尾贴上putIfAbsent()源码。所以 

seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;

这里用 == null判断是否存在于map了,若存在则返回false,不存在则为true。以用来达到去重的目的。


 
  1. default V putIfAbsent(K key, V value) {

  2. V v = get(key);

  3. if (v == null) {

  4. v = put(key, value);

  5. }

  6.  
  7. return v;

  8. }

注:putIfAbsent()定义在Map接口中,是默认方法。

    原文作者:原创学无止尽
    原文地址: https://blog.csdn.net/liuj2511981/article/details/82180776
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞