Stream提供的distinct()方法只能去除重复的对象,却无法根据指定的对象属性进行去重。在java8之前,大多数都是使用for+if来去重对象的属性.有了stream之后就不需要写那么复杂有难懂的代码了,

第一种方式如下,

List<User> unique = users.stream().collect(
        collectingAndThen(toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getId()))),
            ArrayList::new));

上述代码是根据User对象属性的id来去重,这种方法唯一的坏处就是执行了流的终端操作,流就不能继续使用了。

第二种方式如下

user= users.stream()
        .filter(distinctByKey(o -> o.getId()))
        .collect(Collectors.toList());

如上,distinctByKey是自定义方法,如下

public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
    Map<Object, Boolean> seen = new ConcurrentHashMap<>();
    return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
  }

首先是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。以用来达到去重的目的

default V putIfAbsent(K key, V value) {  //注:putIfAbsent()定义在Map接口中,是默认方法。
        V v = get(key);
        if (v == null) {
            v = put(key, value);
        }
 
        return v;
    }

  



版权声明:本文为kanong520原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/kanong520/p/9454694.html