赞
踩
在 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
的操作只会根据 Object
的equal()
和 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
本身由于功能十分强大,灵活运用可以帮助我们实现快速的开发。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。