java8使用stream流自定义去重 distinct方法和自定义Predicate

在 JDK8 中引入了 Stream 的编程风格,通过灵活运用该风格可以帮助我们实现更加便捷的数据处理操作。今天这里讲解一下 Stream 中的 distinct() 实现去重以及如何通过filter()设计自定义的去重逻辑。

final int[] distinct = Stream.of(1, 1, 1, 2, 2, 3, 3, 4, 4, 5)
        // 根据 Objects.equal() 进行去重
        .distinct()
        .mapToInt(Integer::intValue).toArray();
System.out.println(Arrays.toString(distinct));

根据当前流返回一个包含不重复元素 (根据 Object.equals(object) 判重) 流。
对于有序流,该操作对于非重复元素的选择是稳定的 (每次去重的都是相同的元素)。而对于无序流,该操作无法保证去重的稳定性 (每次去重无法保证元素相同)。

比如,上面代码中我有三个 1,对于上述代码不论我运行多少次,distinct() 都会保留第一个 1,而将后面两个 1 当做重复值移除掉。如果我无法保证流中元素的顺序的话,那么我就无法保证每次移除的重复值是稳定的。因此不论顺序如何,distinct()都只会保留第一个出现的非重复的元素,而移除掉剩下的与该元素重复的元素。

直接使用 disctinct() 的话 Stream 的操作只会根据 Objectequal()hashCode() 对元素进行重复判断,但在一些情况下我们希望实现自定义的 distinct() 时,就需要通过 filter()自己进行设计了。

下面我有一个 User 对象:

@Data
public class User{
	private Integer id;
	private String name;
	private Integer age;
	private String addr;
}

此时我希望能够根据 User 对象的 name 进行去重,而非按照 User 对象的 hashCode() 去重,那么对应的代码如下:

final List<User> users = Arrays.asList(
  new User(1, "yuxin", 26, "beijing"),
  new User(2, "chunfeng", 26, "tianjing"),
  new User(3, "feiyang", 26, "wuzhou"),
  new User(3, "feiyang", 27, "wuzhou"),
  new User(4, "fei", 26, "sichuan"),
  new User(5, "yi", 26, "australia")
);
final Map<String, User> map = new ConcurrentHashMap<>();
final List<Object> ret = users.stream()
        // 通过 map 对数据进行去重,map 只用在这里进行去重
        .filter(user -> map.put(user.getName(), user) == null)
        .collect(Collectors.toList());
ret.forEach(System.out::println);

可以看到,我在 Stream 之外定义了一个 map 并在filter操作中尝试通过map 进行去重。如果不想将去重用的 map 暴露在 Stream 之外,我们还可以使用静态方法对 Predicate 进行封装 :

/**
 * 自定义去重
 * @return func
 */
private static Predicate<User> customDistinct() {
    final Map<String, User> map = new ConcurrentHashMap<>();
    return user -> map.put(user.getName(), user) == null;
}

final List<User> users = Arrays.asList(
        new User(1, "yuxin", 26, "beijing"),
        new User(2, "chunfeng", 26, "tianjing"),
        new User(3, "feiyang", 26, "wuzhou"),
        new User(3, "feiyang", 27, "wuzhou"),
        new User(4, "fei", 26, "sichuan"),
        new User(5, "yi", 26, "australia")
);
final List<Object> ret = users.stream()
        // 这里调用上面的方法,获取去重方法
        .filter(customDistinct())
        .collect(Collectors.toList());
ret.forEach(System.out::println);

对于去重使用的 Map,其实并没有限定,如果只是使用到了 Stream 而非 parallelStream 的话 HashMap 即足以,但如果用到了 parallelStream 的话就需要考虑到去重过程中涉及到的并发问题,使用 ConcurrentHashMap 会比较合适一点,由于 Set 数据结构本身也比较适合用来去重,我们同样可以使用 set 实现去重而没有必要保存元素本身。如何选型就要看开发者自己的实际场景需要了。

// 使用 HashSet 实现去重,非并发场景下适用
private static Predicate<User> customDistinct() {
    final Set<String> set = new HashSet<>();
    return user -> set.add(user.getName());
}
// 并发场景使用 ConcurrentHashSet
private static Predicate<User> customDistinct() {
    final Set<String> set = new ConcurrentHashSet<>();
    return user -> set.add(user.getName());
}
总结

以上是通过 Stream 进行去重的一些小小总结,如果您有什么疑问或者补充可以在评论区留言。而对于 Stream 本身由于功能十分强大,灵活运用可以帮助我们实现快速的开发。

参考资料

jdk8 的 stream 去重函数 distinct
java stream distinc

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值