!1335 优化BeanUtil.copyToList拷贝较大数据量的性能问题

Merge pull request !1335 from IzayoiYurin/v5-dev
This commit is contained in:
Looly 2025-04-26 05:17:24 +00:00 committed by Gitee
commit 3ff79b3f23
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
4 changed files with 75 additions and 5 deletions

View File

@ -181,7 +181,8 @@ public class BeanDesc implements Serializable {
prop.setter = propIgnoreCase.setter;
}
}
// 所有属性完成填充后的初始化逻辑
prop.initialize();
return prop;
}

View File

@ -36,6 +36,22 @@ public class PropDesc {
* Setter方法
*/
protected Method setter;
/**
* get方法或字段上有无transient关键字和@Transient注解
*/
private boolean transientForGet;
/**
* set方法或字段上有无transient关键字和@Transient注解
*/
private boolean transientForSet;
/**
* 检查set方法和字段有无@PropIgnore注解
*/
private boolean ignoreGet;
/**
* 检查set方法和字段有无@PropIgnore注解
*/
private boolean ignoreSet;
/**
* 构造<br>
@ -51,6 +67,17 @@ public class PropDesc {
this.setter = ClassUtil.setAccessible(setter);
}
/**
* 在对象的所有属性设置完成后执行初始化逻辑
* <p>
* 预先计算transient关键字和@Transient注解{@link PropIgnore}注解信息
*/
public void initialize() {
transientForGet = isTransientForGet();
transientForSet = isTransientForSet();
ignoreGet = isIgnoreGet();
ignoreSet = isIgnoreSet();
}
/**
* 获取字段名如果存在Alias注解读取注解的值作为名称
*
@ -137,12 +164,12 @@ public class PropDesc {
}
// 检查transient关键字和@Transient注解
if (checkTransient && isTransientForGet()) {
if (checkTransient && transientForGet) {
return false;
}
// 检查@PropIgnore注解
return false == isIgnoreGet();
return false == ignoreGet;
}
/**
@ -207,12 +234,12 @@ public class PropDesc {
}
// 检查transient关键字和@Transient注解
if (checkTransient && isTransientForSet()) {
if (checkTransient && transientForSet) {
return false;
}
// 检查@PropIgnore注解
return false == isIgnoreSet();
return false == ignoreSet;
}
/**

View File

@ -9,6 +9,7 @@ import cn.hutool.core.lang.Editor;
import cn.hutool.core.lang.func.Func1;
import cn.hutool.core.lang.func.LambdaUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
@ -91,6 +92,14 @@ public class CopyOptions implements Serializable {
return null;
}
// 快速处理简单值类型的转换
if (type instanceof Class){
Class<?> targetType = (Class<?>) type;
if (ClassUtil.isSimpleValueType(targetType) && targetType.isInstance(value)) {
return targetType.cast(value);
}
}
if (value instanceof IJSONTypeConverter) {
return ((IJSONTypeConverter) value).toBean(ObjectUtil.defaultIfNull(type, Object.class));
}

View File

@ -5,6 +5,8 @@ import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.bean.copier.ValueProvider;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.StopWatch;
import cn.hutool.core.lang.Console;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.map.MapBuilder;
import cn.hutool.core.map.MapUtil;
@ -707,6 +709,37 @@ public class BeanUtilTest {
}
@Test
public void copyLargeListTest(){
final SubPerson person = new SubPerson();
person.setName("测试A11");
person.setAge(14);
person.setOpenid("11213232");
person.setId(UUID.randomUUID());
person.setSubName("sub名字");
person.setSlow(true);
person.setDate(LocalDateTime.now());
person.setDate2(LocalDate.now());
final List<SubPerson> list = new ArrayList<>();
CollUtil.padRight(list, 1000, person);
// 预先构建一次缓存防止干扰
BeanUtil.copyProperties(person, new SubPerson2());
// org.springframework.beans.BeanUtils.copyProperties(new SubPerson(), new SubPerson2());
Console.log("copy bean size: {}\n", list.size());
final StopWatch stopWatch = new StopWatch();
stopWatch.start("BeanUtil#copyToList");
List<SubPerson2> copyList = BeanUtil.copyToList(list, SubPerson2.class);
// list.forEach(item -> org.springframework.beans.BeanUtils.copyProperties(item, new SubPerson2()));
stopWatch.stop();
Console.log(stopWatch.prettyPrint());
assertEquals(copyList.size(),list.size());
}
@Test
public void toMapTest() {
// 测试转map的时候返回key