在针对page或者list接口的时候,需要转换对应的属性值是比较麻烦的。需要我们手动遍历对象,然后编写对应的转换方法。在多个转换的字段的时候,代码是非常长的。本工具可以一行实现字段转换。我个人觉得较为优雅。
初始版
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.lang.reflect.Field;
/**
* 字段转换工具类
* 作者: zanglikun
* 日期: 2024/7/19 上午11:16
* 版权所有 © zanglikun.com
*/
public class ConvertFiledUtil {
/**
* 使用键值对映射更新对象列表中的字段
*
* @param list 目标列表
* @param map 键值对映射 注意Key的类型
* @param keyExtractor 提取键的函数 注意与Key的类型是否一致
* @param valueSetter 设置值的函数
* @param <T> 列表中对象的类型
* @param <K> 键的类型
* @param <V> 值的类型
*/
public static <T, K, V> void updateListField(List<T> list, Map<K, V> map,
Function<T, K> keyExtractor, BiConsumer<T, V> valueSetter) {
for (T item : list) {
K key = keyExtractor.apply(item);
if (map.containsKey(key)) {
V value = map.get(key);
valueSetter.accept(item, value);
}
}
}
/**
* 使用反射更新对象列表中的字段
*
* @param list 目标列表
* @param map 键值对映射
* @param mapKeyField 用于映射的字段名
* @param replaceField 要更新的字段名
* @param <T> 列表中对象的类型
* @param <K> 键的类型
* @param <V> 值的类型
*/
public static <T, K, V> void updateListField(List<T> list, Map<K, V> map, String mapKeyField, String replaceField) {
for (T item : list) {
try {
// 通过反射获取 mapKeyField 字段的值
Field keyField = item.getClass().getDeclaredField(mapKeyField);
keyField.setAccessible(true);
@SuppressWarnings("unchecked")
K key = (K) keyField.get(item);
// 从 map 中获取对应的值
V value = map.get(key);
// 通过反射设置 replaceField 字段的值
Field valueField = item.getClass().getDeclaredField(replaceField);
valueField.setAccessible(true);
valueField.set(item, value);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
初始版使用代码演示
@Override
public List<AccountIpRecord> selectAccountIpRecordList(AccountIpRecord accountIpRecord) {
// 1、获取账户IP记录列表
List<AccountIpRecord> originList = accountIpRecordMapper.selectAccountIpRecordList(accountIpRecord);
// 2.1、提取唯一的 charId
List<Integer> charIds = originList.stream()
.map(ele -> Integer.parseInt(ele.getCharid().toString())).distinct()
.collect(Collectors.toList());
// 2.2、获取角色名称数据
List<MapKeyValue> charNameData = CollectionUtils.isEmpty(charIds)
? Collections.emptyList()
: charsMapper.getCharNameByCharIds(charIds);
// 2.3、将角色名称数据转换为映射
Map<Long, String> charNameMap = charNameData.stream()
.collect(Collectors.toMap(mapKeyValue -> Long.valueOf(mapKeyValue.getMapKey()), MapKeyValue::getMapValue));
// 2.4、更新角色名称
ConvertFiledUtil.updateListField(originList, charNameMap, AccountIpRecord::getCharid, AccountIpRecord::setCharName);
// 3.1、提取唯一的客户端IP
List<String> ipList = originList.stream().map(AccountIpRecord::getClientIp).distinct().collect(Collectors.toList());
// 3.2、获取IP地区映射
Map<String, String> ipRegionMap = Ip2AddressForJar.getContryCityHasFromatMap(ipList);
// 3.3、更新客户端地址
ConvertFiledUtil.updateListField(originList, ipRegionMap, AccountIpRecord::getClientIp, AccountIpRecord::setClientAddr);
return originList;
}
升级版优化
主要优化方向
1、定义线程安全的缓存Fileds,自主选择可选择减少反射性能消耗。
2、支持一次修改多个字段。(构造起来较为麻烦,不推荐)。
3、不想使用lambda,可直接使用字段名。
使用推荐:
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* 字段转换工具类 - 增强版
* 提供多种方式进行对象字段的转换和更新,支持缓存、异常处理、类型安全等特性
*
* @author zanglikun
* @version 2.1
* @since 2024/7/19
*/
public final class ConvertFieldUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(ConvertFieldUtil.class);
// Key 是类名+"." + 字段名,Value 是对应的 Field 对象
private static final Map<String, Field> FIELD_CACHE = new ConcurrentHashMap<>();
/**
* 私有构造函数,防止实例化
* 如果尝试实例化,将抛出 UnsupportedOperationException 异常。
* 1. 这样可以避免不必要的内存开销。
* 2. 也可以防止用户误用该类的实例化。
* 3. 这种设计模式在 Java 中被广泛使用,符合最佳实践,有助于代码的可读性和可维护性。
*/
private ConvertFieldUtil() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
// 操作结果统计
public static class UpdateResult {
private final int totalItems;
private final int successCount;
private final int failureCount;
private final List<String> errors;
public UpdateResult(int totalItems, int successCount, int failureCount, List<String> errors) {
this.totalItems = totalItems;
this.successCount = successCount;
this.failureCount = failureCount;
this.errors = Collections.unmodifiableList(new ArrayList<>(errors));
}
public int getTotalItems() {
return totalItems;
}
public int getSuccessCount() {
return successCount;
}
public int getFailureCount() {
return failureCount;
}
public List<String> getErrors() {
return errors;
}
public boolean hasErrors() {
return failureCount > 0;
}
public String getErrorsAsString() {
return String.join(System.lineSeparator(), errors);
}
@Override
public String toString() {
return String.format("UpdateResult{total=%d, success=%d, failure=%d, errors=%d}",
totalItems, successCount, failureCount, errors.size());
}
}
/**
* 自定义异常类
*/
public static class FieldUpdateException extends RuntimeException {
public FieldUpdateException(String message, Throwable cause) {
super(message, cause);
}
public FieldUpdateException(String message) {
super(message);
}
}
/**
* 使用函数式接口更新对象列表中的字段(推荐方式)
*
* @param list 目标列表,不能为null
* @param map 键值对映射,不能为null
* @param keyExtractor 提取键的函数,不能为null
* @param valueSetter 设置值的函数,不能为null
* @param <T> 列表中对象的类型
* @param <K> 键的类型
* @param <V> 值的类型
* @return 更新结果统计
* @throws FieldUpdateException 当参数为null时
*/
public static <T, K, V> UpdateResult updateListWithFunction(
List<T> list,
Map<K, V> map,
Function<T, K> keyExtractor,
BiConsumer<T, V> valueSetter) {
return updateListWithFunction(list, map, keyExtractor, valueSetter, null);
}
/**
* 使用函数式接口更新对象列表中的字段,支持条件过滤
*
* @param list 目标列表,不能为null
* @param map 键值对映射,不能为null
* @param keyExtractor 提取键的函数,不能为null
* @param valueSetter 设置值的函数,不能为null
* @param filter 过滤条件,为null时不过滤
* @param <T> 列表中对象的类型
* @param <K> 键的类型
* @param <V> 值的类型
* @return 更新结果统计
* @throws FieldUpdateException 当必要参数为null时
*/
public static <T, K, V> UpdateResult updateListWithFunction(
List<T> list,
Map<K, V> map,
Function<T, K> keyExtractor,
BiConsumer<T, V> valueSetter,
Predicate<T> filter) {
// 参数验证
validateNotNull(list, "list");
validateNotNull(map, "map");
validateNotNull(keyExtractor, "keyExtractor");
validateNotNull(valueSetter, "valueSetter");
if (list.isEmpty() || map.isEmpty()) {
return new UpdateResult(list.size(), 0, 0, Collections.emptyList());
}
final List<String> errors = Collections.synchronizedList(new ArrayList<>());
final Predicate<T> finalFilter = filter != null ? filter : item -> true;
long successCount = list.stream()
.filter(finalFilter)
.map(item -> {
try {
K key = keyExtractor.apply(item);
V value = (key != null) ? map.get(key) : null;
if (value != null) {
valueSetter.accept(item, value);
return true;
}
return false;
} catch (Exception e) {
String errorMsg = String.format("Failed to update item %s: %s", item, e.getMessage());
errors.add(errorMsg);
LOGGER.warn(errorMsg, e);
return false;
}
})
.filter(Boolean::booleanValue)
.count();
return new UpdateResult(list.size(), (int) successCount, errors.size(), errors);
}
/**
* 使用反射更新对象列表中的字段(带缓存优化)
*
* @param list 目标列表,不能为null
* @param map 键值对映射,不能为null
* @param mapKeyField 用于映射的字段名,不能为null或空字符串
* @param replaceField 要更新的字段名,不能为null或空字符串
* @param <T> 列表中对象的类型
* @param <K> 键的类型
* @param <V> 值的类型
* @return 更新结果统计
* @throws FieldUpdateException 当参数无效时
*/
public static <T, K, V> UpdateResult updateListWithReflection(
List<T> list,
Map<K, V> map,
String mapKeyField,
String replaceField) {
// 参数验证
validateNotNull(list, "list");
validateNotNull(map, "map");
validateNotNullOrEmpty(mapKeyField, "mapKeyField");
validateNotNullOrEmpty(replaceField, "replaceField");
if (list.isEmpty() || map.isEmpty()) {
return new UpdateResult(list.size(), 0, 0, Collections.emptyList());
}
final List<String> errors = Collections.synchronizedList(new ArrayList<>());
long successCount = list.stream()
.map(item -> {
try {
Field keyField = getFieldWithCache(item.getClass(), mapKeyField);
@SuppressWarnings("unchecked")
K key = (K) getFieldValue(keyField, item);
V value = (key != null) ? map.get(key) : null;
if (value != null) {
Field valueField = getFieldWithCache(item.getClass(), replaceField);
setFieldValue(valueField, item, value);
return true;
}
return false;
} catch (Exception e) {
String errorMsg = String.format("Failed to update item with key field '%s' and replace field '%s': %s",
mapKeyField, replaceField, e.getMessage());
errors.add(errorMsg);
LOGGER.warn(errorMsg, e);
return false;
}
})
.filter(Boolean::booleanValue)
.count();
return new UpdateResult(list.size(), (int) successCount, errors.size(), errors);
}
/**
* 批量更新多个字段(此方法不建议直接调用,如需使用,可参考demo4)
*
* @param list 目标列表
* @param fieldMappings 字段映射配置 Map<源字段名, 目标字段名>
* @param dataMap 数据映射 Map<键值, Map<字段名, 字段值>>
* @param keyField 键字段名
* @param <T> 列表中对象的类型
* @param <K> 键的类型
* @return 更新结果统计
*/
public static <T, K> UpdateResult updateMultipleFields(
List<T> list,
Map<String, String> fieldMappings,
Map<K, Map<String, Object>> dataMap,
String keyField) {
validateNotNull(list, "list");
validateNotNull(fieldMappings, "fieldMappings");
validateNotNull(dataMap, "dataMap");
validateNotNullOrEmpty(keyField, "keyField");
if (list.isEmpty() || fieldMappings.isEmpty() || dataMap.isEmpty()) {
return new UpdateResult(list.size(), 0, 0, Collections.emptyList());
}
final List<String> errors = Collections.synchronizedList(new ArrayList<>());
long successCount = list.stream()
.map(item -> {
try {
Field keyFieldObj = getFieldWithCache(item.getClass(), keyField);
@SuppressWarnings("unchecked")
K key = (K) getFieldValue(keyFieldObj, item);
Map<String, Object> fieldValues = (key != null) ? dataMap.get(key) : null;
if (fieldValues == null) {
return false;
}
boolean updated = false;
for (Map.Entry<String, String> mapping : fieldMappings.entrySet()) {
String sourceField = mapping.getKey();
String targetField = mapping.getValue();
if (fieldValues.containsKey(sourceField)) {
Field targetFieldObj = getFieldWithCache(item.getClass(), targetField);
Object value = fieldValues.get(sourceField);
setFieldValue(targetFieldObj, item, value);
updated = true;
}
}
return updated;
} catch (Exception e) {
String errorMsg = String.format("Failed to update multiple fields for item: %s", e.getMessage());
errors.add(errorMsg);
LOGGER.warn(errorMsg, e);
return false;
}
})
.filter(Boolean::booleanValue)
.count();
return new UpdateResult(list.size(), (int) successCount, errors.size(), errors);
}
/**
* 批量更新多个字段(直接映射版)
* <p>
* 此版本假定数据Map中的key直接对应目标对象的字段名,无需额外的映射关系。
*
* @param list 目标列表
* @param dataMap 数据映射 Map<键值, Map<字段名, 字段值>>
* @param keyField 键字段名
* @param <T> 列表中对象的类型
* @param <K> 键的类型
* @return 更新结果统计
*/
public static <T, K> UpdateResult updateMultipleFields(
List<T> list,
Map<K, Map<String, Object>> dataMap,
String keyField) {
validateNotNull(list, "list");
validateNotNull(dataMap, "dataMap");
validateNotNullOrEmpty(keyField, "keyField");
if (list.isEmpty() || dataMap.isEmpty()) {
return new UpdateResult(list.size(), 0, 0, Collections.emptyList());
}
// 从dataMap的第一个非空value中提取所有key作为字段映射
Map<String, String> fieldMappings = dataMap.values().stream()
.filter(Objects::nonNull)
.flatMap(m -> m.keySet().stream())
.distinct()
.collect(Collectors.toMap(Function.identity(), Function.identity()));
if (fieldMappings.isEmpty()) {
return new UpdateResult(list.size(), 0, 0, Collections.emptyList());
}
return updateMultipleFields(list, fieldMappings, dataMap, keyField);
}
// ========== 私有辅助方法 ==========
/**
* 获取字段(带缓存),会递归查找父类字段
*/
private static Field getFieldWithCache(Class<?> clazz, String fieldName) {
String cacheKey = clazz.getName() + "." + fieldName;
return FIELD_CACHE.computeIfAbsent(cacheKey, key -> {
Class<?> current = clazz;
while (current != null) {
try {
Field field = current.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
current = current.getSuperclass();
}
}
throw new FieldUpdateException("Field not found: " + fieldName + " in class " + clazz.getName() + " and its superclasses.");
});
}
/**
* 获取字段值
*/
private static Object getFieldValue(Field field, Object obj) throws IllegalAccessException {
return field.get(obj);
}
/**
* 设置字段值(带类型检查)
*/
private static void setFieldValue(Field field, Object obj, Object value) throws IllegalAccessException {
// 基本类型检查
if (value != null && !ClassUtils.isAssignable(value.getClass(), field.getType())) {
throw new IllegalArgumentException(
String.format("Cannot assign value of type %s to field %s of type %s",
value.getClass().getName(), field.getName(), field.getType().getName()));
}
field.set(obj, value);
}
/**
* 验证参数不为null
*/
private static void validateNotNull(Object obj, String paramName) {
if (obj == null) {
throw new FieldUpdateException("Parameter '" + paramName + "' cannot be null");
}
}
/**
* 验证字符串参数不为null或空
*/
private static void validateNotNullOrEmpty(String str, String paramName) {
if (StringUtils.isBlank(str)) {
throw new FieldUpdateException("Parameter '" + paramName + "' cannot be null or empty");
}
}
/**
* 清空字段缓存(用于内存管理或测试场景)
*/
public static void clearFieldCache() {
FIELD_CACHE.clear();
LOGGER.info("Field cache cleared.");
}
/**
* 获取缓存统计信息
*/
public static String getCacheStats() {
return String.format("Field cache size: %d entries.", FIELD_CACHE.size());
}
}
演示Demo
1、demoUpdateWithFunction(): 推荐的使用方式,类型安全且性能高
2、demoUpdateWithReflection():您无法或不想使用Lambda表达式,而是希望通过字段名称(字符串)来操作时使用。
3、demoUpdateMultipleFields():非常不推荐,构造参数非常麻烦!
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* ConvertFieldUtil 工具类使用演示
*
* @author zanglikun
* @version 1.0
* @since 2025/07/24
*/
public class ConvertFieldUtilDemo {
// 内部静态类,用于演示
static class User {
private final Long id;
private final String username;
private String role;
private final Long deptId;
private String deptName;
public User(Long id, String username, String role, Long deptId) {
this.id = id;
this.username = username;
this.role = role;
this.deptId = deptId;
}
// Getters and Setters
public Long getId() { return id; }
public String getUsername() { return username; }
public String getRole() { return role; }
public Long getDeptId() { return deptId; }
public String getDeptName() { return deptName; }
public void setDeptName(String deptName) { this.deptName = deptName; }
public void setRole(String role) { this.role = role; }
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", role='" + role + '\'' +
", deptId=" + deptId +
", deptName='" + deptName + '\'' +
'}';
}
}
public static void main(String[] args) {
System.out.println("--- ConvertFieldUtil Demo ---");
// --- 1. 使用函数式接口 updateListWithFunction ---
demoUpdateWithFunction();
// --- 2. 使用反射 updateListWithReflection ---
demoUpdateWithReflection();
// --- 3. 批量更新多个字段(简化版) demoUpdateMultipleFieldsSimplified ---
demoUpdateMultipleFieldsSimplified();
}
private static List<User> createSampleUserList() {
List<User> userList = new ArrayList<>();
userList.add(new User(1L, "admin", "admin", 100L));
userList.add(new User(2L, "ry", "developer", 101L));
userList.add(new User(3L, "test", "tester", 102L));
userList.add(new User(4L, "extra", "guest", 103L));
return userList;
}
/**
* 演示 updateListWithFunction
*/
private static void demoUpdateWithFunction() {
System.out.println("\n--- 1. demoUpdateWithFunction ---");
List<User> userList = createSampleUserList();
System.out.println("更新前: " + userList);
// 准备一个部门ID到部门名称的映射
Map<Long, String> deptMap = new HashMap<>();
deptMap.put(100L, "总经办");
deptMap.put(101L, "研发部门");
// 注意:没有102L的映射,用于测试未匹配的情况
// 调用工具类方法,通过部门ID (deptId) 来更新部门名称 (deptName)
ConvertFieldUtil.UpdateResult result = ConvertFieldUtil.updateListWithFunction(
userList,
deptMap, // 第三个参数类型=Map的Key,第四个参数类型=Map的Value类型
User::getDeptId, // keyExtractor: 使用方法引用从User对象中获取deptId
User::setDeptName // valueSetter: 使用方法引用将Map中的值设置到deptName字段
);
System.out.println("更新后: " + userList);
System.out.println("更新结果: " + result);
}
/**
* 演示 updateListWithReflection
*/
private static void demoUpdateWithReflection() {
System.out.println("\n--- 2. demoUpdateWithReflection ---");
List<User> userList = createSampleUserList();
System.out.println("更新前: " + userList);
// 准备一个用户名到角色的映射
Map<String, String> roleMap = new HashMap<>();
roleMap.put("admin", "超级管理员");
roleMap.put("ry", "普通用户");
roleMap.put("non_exist", "不存在的用户"); // 用于测试未匹配
// 调用工具类方法,通过反射,使用 username 字段匹配,更新 role 字段
ConvertFieldUtil.UpdateResult result = ConvertFieldUtil.updateListWithReflection(
userList,
roleMap,
"username", // mapKeyField: User对象中用于匹配Map Key的字段名
"role" // replaceField: User对象中需要被更新的字段名
);
System.out.println("更新后: " + userList);
System.out.println("更新结果: " + result);
}
/**
* 演示 updateMultipleFields
*/
private static void demoUpdateMultipleFields() {
System.out.println("\n--- 3. demoUpdateMultipleFields ---");
List<User> userList = createSampleUserList();
System.out.println("更新前: " + userList);
// 准备一个更复杂的数据源,一个ID可以更新多个字段
Map<Long, Map<String, Object>> dataMap = new HashMap<>();
Map<String, Object> user1Data = new HashMap<>();
user1Data.put("newRole", "系统管理员");
user1Data.put("newDeptName", "总裁办");
dataMap.put(1L, user1Data);
Map<String, Object> user2Data = new HashMap<>();
user2Data.put("newRole", "高级工程师");
user2Data.put("newDeptName", "核心研发部");
dataMap.put(2L, user2Data);
// 定义数据源字段到目标对象字段的映射关系
Map<String, String> fieldMappings = new HashMap<>();
fieldMappings.put("newRole", "role");
fieldMappings.put("newDeptName", "deptName");
// 调用工具类方法,通过用户ID,批量更新 role 和 deptName
ConvertFieldUtil.UpdateResult result = ConvertFieldUtil.updateMultipleFields(
userList,
fieldMappings,
dataMap,
"id" // keyField: User对象中用于匹配dataMap Key的字段名
);
System.out.println("更新后: " + userList);
System.out.println("更新结果: " + result);
}
/**
* 演示 updateMultipleFields 的简化版本
*/
private static void demoUpdateMultipleFieldsSimplified() {
System.out.println("\n--- 4. demoUpdateMultipleFieldsSimplified ---");
List<User> userList = createSampleUserList();
System.out.println("更新前: " + userList);
// 准备数据源,这次Map的Key直接对应User对象的字段名
Map<Long, Map<String, Object>> dataMap = new HashMap<>();
Map<String, Object> user1Data = new HashMap<>();
user1Data.put("role", "系统管理员"); // key "role" 直接对应 User.role
user1Data.put("deptName", "总裁办"); // key "deptName" 直接对应 User.deptName
dataMap.put(1L, user1Data);
Map<String, Object> user2Data = new HashMap<>();
user2Data.put("role", "高级工程师");
user2Data.put("deptName", "核心研发部");
dataMap.put(2L, user2Data);
// 调用简化版的工具类方法,无需提供 fieldMappings
ConvertFieldUtil.UpdateResult result = ConvertFieldUtil.updateMultipleFields(
userList,
dataMap,
"id" // keyField: User对象中用于匹配dataMap Key的字段名
);
System.out.println("更新后: " + userList);
System.out.println("更新结果: " + result);
}
}
特殊说明:
上述文章均是作者实际操作后产出。烦请各位,请勿直接盗用!转载记得标注原文链接:www.zanglikun.com
第三方平台不会及时更新本文最新内容。如果发现本文资料不全,可访问本人的Java博客搜索:标题关键字。以获取最新全部资料 ❤
免责声明: 本站文章旨在总结学习互联网技术过程中的经验与见解。任何人不得将其用于违法或违规活动!所有违规内容均由个人自行承担,与作者无关。
第三方平台不会及时更新本文最新内容。如果发现本文资料不全,可访问本人的Java博客搜索:标题关键字。以获取最新全部资料 ❤
免责声明: 本站文章旨在总结学习互联网技术过程中的经验与见解。任何人不得将其用于违法或违规活动!所有违规内容均由个人自行承担,与作者无关。
