当前位置:   article > 正文

java8使用stream流自定义去重 distinct方法和自定义Predicate_stream.distinct 自定义对象去重

stream.distinct 自定义对象去重

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));
  • 1
  • 2
  • 3
  • 4
  • 5

根据当前流返回一个包含不重复元素 (根据 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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

此时我希望能够根据 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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

可以看到,我在 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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

对于去重使用的 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());
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
总结

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

参考资料

jdk8 的 stream 去重函数 distinct
java stream distinc

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/442108
推荐阅读
  

闽ICP备14008679号