package com.zibo.utils;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 对象去重工具类
* 提供了基于对象字段的灵活去重功能
*
* @author zibo
* @date 2024/01/13
*/
public class DeduplicationUtils {
private DeduplicationUtils() {
}
/**
* 根据对象的一个或多个字段进行去重
* 默认不保持原有列表顺序
* -
* 使用示例:
* List<User> users = Arrays.asList(
* new User("张三", 20),
* new User("李四", 25),
* new User("张三", 20)
* );
* // 根据name和age去重
* List<User> unique = deduplicate(users, User::getName, User::getAge);
*
* @param list 要进行去重的列表
* @param keyExtractors 用于提取去重关键字段的函数集合,可以是对象的任意字段的getter方法引用
* @param <T> 对象类型
* @return 去重后的新列表
*/
@SafeVarargs
public static <T> List<T> deduplicate(List<T> list, Function<? super T, ?>... keyExtractors) {
return deduplicate(list, false, keyExtractors);
}
/**
* 根据对象的一个或多个字段进行去重,可选择是否保持原有列表顺序
* -
* 使用示例:
* List<User> users = Arrays.asList(
* new User("张三", 20),
* new User("李四", 25),
* new User("张三", 20)
* );
* // 根据name去重,并保持原有顺序
* List<User> unique = deduplicate(users, true, User::getName);
*
* @param list 要进行去重的列表
* @param maintainOrder 是否保持原有列表顺序,true表示保持原顺序,false则不保证顺序
* @param keyExtractors 用于提取去重关键字段的函数集合,可以是对象的任意字段的getter方法引用
* @param <T> 对象类型
* @return 去重后的新列表
*/
@SafeVarargs
public static <T> List<T> deduplicate(List<T> list, boolean maintainOrder, Function<? super T, ?>... keyExtractors) {
// 空列表处理
if (list == null || list.isEmpty()) {
return new ArrayList<>();
}
// 如果没有指定去重字段,则使用对象本身的equals和hashCode方法进行去重
if (keyExtractors == null || keyExtractors.length == 0) {
if (maintainOrder) {
// 使用Stream的distinct方法保持顺序去重
return list.stream().distinct().collect(Collectors.toList());
} else {
// 使用HashSet去重(不保证顺序)
Set<T> set = new HashSet<>(list);
return new ArrayList<>(set);
}
}
// 使用LinkedHashMap或ConcurrentHashMap进行去重,通过自定义key实现多字段组合去重
if (maintainOrder) {
// 使用LinkedHashMap保持原有顺序
return list.stream()
.collect(Collectors.collectingAndThen(
Collectors.toMap(
t -> generateKey(t, keyExtractors), // 生成组合key
Function.identity(), // 值为对象本身
(t1, t2) -> t1, // 遇到重复时保留第一个
LinkedHashMap::new // 使用LinkedHashMap保持顺序
),
map -> new ArrayList<>(map.values())
));
} else {
// 使用HashMap
return list.stream()
.collect(Collectors.collectingAndThen(
Collectors.toMap(
t -> generateKey(t, keyExtractors), // 生成组合key
Function.identity(), // 值为对象本身
(t1, t2) -> t1, // 遇到重复时保留第一个
HashMap::new // 使用HashMap即可
),
map -> new ArrayList<>(map.values())
));
}
}
/**
* 根据提供的字段提取器生成组合key
* 将多个字段的值组合成一个List作为去重的key
*
* @param t 对象
* @param keyExtractors 字段提取器数组
* @param <T> 对象类型
* @return 组合后的key列表
*/
private static <T> List<Object> generateKey(T t, Function<? super T, ?>[] keyExtractors) {
return Arrays.stream(keyExtractors)
.map(extractor -> extractor.apply(t))
.collect(Collectors.toList());
}
}
在日常开发中,我们经常会遇到需要对对象列表进行去重的需求。比如,从数据库中查询出一批用户数据,但其中可能存在重复记录,我们需要根据某些字段(如姓名、年龄等)来去除重复项。今天,我将分享一个非常实用的Java工具类——DeduplicationUtils
,它可以帮助我们轻松实现基于对象字段的灵活去重。
DeduplicationUtils
是一个专门用于对象去重的工具类,它提供了基于对象字段的灵活去重功能。通过这个工具类,我们可以根据一个或多个字段对对象列表进行去重,并且可以选择是否保持原有列表的顺序。
User::getName
)。假设我们有一个User
类,包含name
和age
两个字段:
public class User {
private String name;
private int age;
// 构造方法、getter和setter省略
}
现在,我们有一个User
对象的列表,其中包含重复项:
List<User> users = Arrays.asList(
new User("张三", 20),
new User("李四", 25),
new User("张三", 20)
);
我们可以使用DeduplicationUtils
来根据name
和age
字段进行去重:
List<User> uniqueUsers = DeduplicationUtils.deduplicate(users, User::getName, User::getAge);
如果我们希望保持原有列表的顺序,可以这样调用:
List<User> uniqueUsers = DeduplicationUtils.deduplicate(users, true, User::getName, User::getAge);
DeduplicationUtils
的核心逻辑是通过Stream
API和Collectors.toMap
方法来实现去重。具体步骤如下:
Collectors.toMap
方法,将对象列表转换为一个Map
,其中Key是生成的组合Key,Value是对象本身。如果遇到重复的Key,则保留第一个对象。LinkedHashMap
来存储结果;否则使用HashMap
。以下是DeduplicationUtils
的核心代码:
public static <T> List<T> deduplicate(List<T> list, boolean maintainOrder, Function<? super T, ?>... keyExtractors) {
if (list == null || list.isEmpty()) {
return new ArrayList<>();
}
if (keyExtractors == null || keyExtractors.length == 0) {
if (maintainOrder) {
return list.stream().distinct().collect(Collectors.toList());
} else {
Set<T> set = new HashSet<>(list);
return new ArrayList<>(set);
}
}
if (maintainOrder) {
return list.stream()
.collect(Collectors.collectingAndThen(
Collectors.toMap(
t -> generateKey(t, keyExtractors),
Function.identity(),
(t1, t2) -> t1,
LinkedHashMap::new
),
map -> new ArrayList<>(map.values())
));
} else {
return list.stream()
.collect(Collectors.collectingAndThen(
Collectors.toMap(
t -> generateKey(t, keyExtractors),
Function.identity(),
(t1, t2) -> t1,
HashMap::new
),
map -> new ArrayList<>(map.values())
));
}
}
private static <T> List<Object> generateKey(T t, Function<? super T, ?>[] keyExtractors) {
return Arrays.stream(keyExtractors)
.map(extractor -> extractor.apply(t))
.collect(Collectors.toList());
}
DeduplicationUtils
是一个非常实用的工具类,它可以帮助我们轻松实现基于对象字段的去重功能。无论是简单的单字段去重,还是复杂的多字段组合去重,它都能胜任。此外,它还提供了保持顺序的选项,满足了不同场景下的需求。
如果你在项目中遇到类似的需求,不妨试试这个工具类,相信它会为你节省不少时间和精力。
希望这篇博客对你有所帮助!如果你有任何问题或建议,欢迎在评论区留言讨论。