说明
在工作了许久之后,也认识了很多对象属性拷贝工具,诸如标题所示的那些工具,于是就想看看哪个工具在对象拷贝方面性能更高,于是乎就有了这篇博文,希望对小伙伴们有点帮助。
由于是测试性能,代码写的比较露骨简单且基本相同,在每个工具类正式测试前,都做了代码预热,为了就是让结果尽量准确,各个工具的测试代码将贴在文末,下面直接附上测试结果。
测试环境
- 系统:macOS 10.14.6、 1.8 GHz Intel Core i5、 8 GB 1600 MHz DDR3
- Java环境:java version “1.8.0_201”
- IDE集成环境:IntelliJ IDEA 2018.3.5
性能测试结果对比
- 预热100次后的结果:
类 | 方法 | 归属 | 单次耗时[ms] | 100万次耗时[ms] | 平均耗时[ms] |
---|---|---|---|---|---|
BeanUtils | copyProperties | Apache | 0.310196 | 30253.140057 | 0.030253 |
DozerBeanMapper | map | Dozer | 0.598338 | 19158.842820 | 0.019158 |
PropertyUtils | copyProperties | Apache | 0.164118 | 16714.877932 | 0.016714 |
BeanUtils | copyProperties | Spring | 0.072082 | 2155.744378 | 0.002155 |
BeanCopier | copy | Cglib | 0.005071 | 58.415201 | 0.000058 |
-
预热100次后结果排行
单次耗时:BeanCopier < BeanUtils(Spring) < PropertyUtils < BeanUtils(Apache) < DozerBeanMapper
100万次耗时:BeanCopier < BeanUtils(Spring) < PropertyUtils < DozerBeanMapper < BeanUtils(Apache)
平均耗时:BeanCopier < BeanUtils(Spring) < PropertyUtils < DozerBeanMapper < BeanUtils(Apache) -
预热1000次后的结果:
类 | 方法 | 归属 | 单次耗时[ms] | 100万次耗时[ms] | 平均耗时[ms] |
---|---|---|---|---|---|
BeanUtils | copyProperties | Apache | 0.088523 | 32556.660915 | 0.032556 |
DozerBeanMapper | map | Dozer | 0.112480 | 17818.511072 | 0.017818 |
PropertyUtils | copyProperties | Apache | 0.055119 | 18046.194031 | 0.018046 |
BeanUtils | copyProperties | Spring | 0.025337 | 1969.376513 | 0.001969 |
BeanCopier | copy | Cglib | 0.001124 | 69.608191 | 0.000069 |
- 预热1000次后结果排行:
单次耗时:BeanCopier < BeanUtils(Spring) < PropertyUtils < BeanUtils(Apache) < DozerBeanMapper
100万次耗时:BeanCopier < BeanUtils(Spring) < DozerBeanMapper < PropertyUtils < BeanUtils(Apache)
平均耗时:BeanCopier < BeanUtils(Spring) < DozerBeanMapper < PropertyUtils < BeanUtils(Apache)
结果分析
- 在两次测试结果中,BeanCopier同学都脱颖而出,以绝对的优势力压群雄,掌声鼓励~
- 预热1000次的执行结果比100次的更接近100万次的平均值
- 结果中可以看出,并不是预热1000次的执行结果就一定比100次的耗时少(感兴趣的可以在内存、GC方面思考一下)。
- 根据上面的结果,优先使用哪个工具就很明显了。当然,各个工具提供的功能不全一样,它们也有自己的特色功能,感兴趣或者有需要的可以去学习一下。
- 至于Apache的BeanUtils就不推荐使用了,Alibaba Java Coding Guidelines这个插件都提示不推荐了(T.T)
- 虽然说BeanCopier表现很出色,但是在易用性方面有点欠缺。可参考封装BeanCopier工具,支持单个对象以及列表拷贝,超高性能简单实用
- DozerBeanMapper工具虽然表现一般,但是它是深拷贝。
测试代码(仅供参考)
- 源对象 OriginBean
import lombok.Getter;
import lombok.Setter;
import java.math.BigDecimal;
import java.math.BigInteger;
@Getter
@Setter
public class OriginBean {
private int intProperty;
private boolean booleanProperty;
private float floatProperty;
private double doubleProperty;
private long longProperty;
private char charProperty;
private byte byteProperty;
private short shortProperty;
private Integer integerProperty;
private Boolean booleanObjProperty;
private Float floatObjProperty;
private Double doubleObjProperty;
private Long longObjProperty;
private Short shortObjProperty;
private Byte byteObjProperty;
private BigInteger bigIntegerProperty;
private BigDecimal bigDecimalProperty;
private String stringProperty;
public OriginBean() {
this.intProperty = 10000;
this.booleanProperty = true;
this.floatProperty = 0.556F;
this.doubleProperty = 10000.221D;
this.longProperty = 99999999999L;
this.charProperty = 'a';
this.byteProperty = 123;
this.shortProperty = 12222;
this.integerProperty = 11111111;
this.booleanObjProperty = Boolean.TRUE;
this.floatObjProperty = 99.32322F;
this.doubleObjProperty = 222121.3232D;
this.longObjProperty = 333333L;
this.shortObjProperty = 12121;
this.byteObjProperty = 121;
this.bigIntegerProperty = new BigInteger("12121212121");
this.bigDecimalProperty = new BigDecimal(212123323323232L);
this.stringProperty = "功盖三分国,名成八阵图。江流石不转,遗恨失吞吴。";
}
}
- 目标对象 TargetBean
import lombok.Getter;
import lombok.Setter;
import java.math.BigDecimal;
import java.math.BigInteger;
@Getter
@Setter
public class TargetBean {
private int intProperty;
private boolean booleanProperty;
private float floatProperty;
private double doubleProperty;
private long longProperty;
private char charProperty;
private byte byteProperty;
private short shortProperty;
private Integer integerProperty;
private Boolean booleanObjProperty;
private Float floatObjProperty;
private Double doubleObjProperty;
private Long longObjProperty;
private Short shortObjProperty;
private Byte byteObjProperty;
private BigInteger bigIntegerProperty;
private BigDecimal bigDecimalProperty;
private String stringProperty;
}
- Apache BeanUtils
import org.apache.commons.beanutils.BeanUtils;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
public class ApacheBeanUtil {
/**
* 预热
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
public static void preheat() throws InvocationTargetException, IllegalAccessException {
List<OriginBean> originBeans = new ArrayList<>();
List<TargetBean> targetBeans = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
originBeans.add(new OriginBean());
targetBeans.add(new TargetBean());
}
for (int i = 0; i < 1000; i++) {
OriginBean originBean = originBeans.get(i);
TargetBean targetBean = targetBeans.get(i);
BeanUtils.copyProperties(targetBean, originBean);
}
}
/**
* 测试单个对象属性拷贝
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
public static void copySingle() throws InvocationTargetException, IllegalAccessException {
OriginBean originBean = new OriginBean();
TargetBean targetBean = new TargetBean();
long start = System.nanoTime();
BeanUtils.copyProperties(targetBean, originBean);
long end = System.nanoTime() - start;
System.out.println(new BigDecimal(end).divide(new BigDecimal(1000000), 6, RoundingMode.HALF_UP) + "[ms]");
}
/**
* 测试列表对象属性拷贝
* @param times
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
public static void copyList(int times) throws InvocationTargetException, IllegalAccessException {
List<OriginBean> originBeans = new ArrayList<>();
List<TargetBean> targetBeans = new ArrayList<>();
for (int i = 0; i < times; i++) {
originBeans.add(new OriginBean());
targetBeans.add(new TargetBean());
}
long start = System.nanoTime();
for (int i = 0; i < times; i++) {
OriginBean originBean = originBeans.get(i);
TargetBean targetBean = targetBeans.get(i);
BeanUtils.copyProperties(targetBean, originBean);
}
long end = System.nanoTime() - start;
System.out.println(new BigDecimal(end).divide(new BigDecimal(1000000), 6, RoundingMode.HALF_UP) + "[ms]");
System.out.println(new BigDecimal(end/times).divide(new BigDecimal(1000000), 6, RoundingMode.HALF_UP) + "[ms]");
}
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
preheat();
copySingle();
copyList(1000 * 1000);
}
}
- Dozer DozerBeanMapper
import org.dozer.DozerBeanMapper;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
public class DozerBeanMapperUtil {
private static final DozerBeanMapper mapper = new DozerBeanMapper();
/**
* 预热
*/
public static void preheat() {
List<OriginBean> originBeans = new ArrayList<>();
List<TargetBean> targetBeans = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
originBeans.add(new OriginBean());
targetBeans.add(new TargetBean());
}
for (int i = 0; i < 1000; i++) {
OriginBean originBean = originBeans.get(i);
TargetBean targetBean = targetBeans.get(i);
mapper.map(originBean, targetBean);
}
}
/**
* 测试单个对象属性拷贝
*/
public static void copySingle(){
OriginBean originBean = new OriginBean();
TargetBean targetBean = new TargetBean();
long start = System.nanoTime();
mapper.map(originBean, targetBean);
long end = System.nanoTime() - start;
System.out.println(new BigDecimal(end).divide(new BigDecimal(1000000), 6, RoundingMode.HALF_UP) + "[ms]");
}
/**
* 测试列表对象属性拷贝
* @param times
*/
public static void copyList(int times) {
List<OriginBean> originBeans = new ArrayList<>();
List<TargetBean> targetBeans = new ArrayList<>();
for (int i = 0; i < times; i++) {
originBeans.add(new OriginBean());
targetBeans.add(new TargetBean());
}
long start = System.nanoTime();
for (int i = 0; i < times; i++) {
OriginBean originBean = originBeans.get(i);
TargetBean targetBean = targetBeans.get(i);
mapper.map(originBean, targetBean);
}
long end = System.nanoTime() - start;
System.out.println(new BigDecimal(end).divide(new BigDecimal(1000000), 6, RoundingMode.HALF_UP) + "[ms]");
System.out.println(new BigDecimal(end/times).divide(new BigDecimal(1000000), 6, RoundingMode.HALF_UP) + "[ms]");
}
public static void main(String[] args) {
preheat();
copySingle();
copyList(1000 * 1000);
}
}
- Apache PropertiesUtils
import org.apache.commons.beanutils.PropertyUtils;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
public class PropertiesUtil {
/**
* 预热
*/
public static void preheat() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
List<OriginBean> originBeans = new ArrayList<>();
List<TargetBean> targetBeans = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
originBeans.add(new OriginBean());
targetBeans.add(new TargetBean());
}
for (int i = 0; i < 1000; i++) {
OriginBean originBean = originBeans.get(i);
TargetBean targetBean = targetBeans.get(i);
PropertyUtils.copyProperties(targetBean, originBean);
}
}
/**
* 测试单个对象属性拷贝
* @throws IllegalAccessException
* @throws NoSuchMethodException
* @throws InvocationTargetException
*/
public static void copySingle() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
OriginBean originBean = new OriginBean();
TargetBean targetBean = new TargetBean();
long start = System.nanoTime();
PropertyUtils.copyProperties(targetBean, originBean);
long end = System.nanoTime() - start;
System.out.println(new BigDecimal(end).divide(new BigDecimal(1000000), 6, RoundingMode.HALF_UP) + "[ms]");
}
/**
* 测试列表对象属性拷贝
* @param times
* @throws IllegalAccessException
* @throws NoSuchMethodException
* @throws InvocationTargetException
*/
public static void copyList(int times) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
List<OriginBean> originBeans = new ArrayList<>();
List<TargetBean> targetBeans = new ArrayList<>();
for (int i = 0; i < times; i++) {
originBeans.add(new OriginBean());
targetBeans.add(new TargetBean());
}
long start = System.nanoTime();
for (int i = 0; i < times; i++) {
OriginBean originBean = originBeans.get(i);
TargetBean targetBean = targetBeans.get(i);
PropertyUtils.copyProperties(targetBean, originBean);
}
long end = System.nanoTime() - start;
System.out.println(new BigDecimal(end).divide(new BigDecimal(1000000), 6, RoundingMode.HALF_UP) + "[ms]");
System.out.println(new BigDecimal(end/times).divide(new BigDecimal(1000000), 6, RoundingMode.HALF_UP) + "[ms]");
}
public static void main(String[] args) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
preheat();
copySingle();
copyList(1000 * 1000);
}
}
- Spring BeanUtils
import org.springframework.beans.BeanUtils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
public class SpringBeanUtil {
/**
* 预热
*/
public static void preheat() {
List<OriginBean> originBeans = new ArrayList<>();
List<TargetBean> targetBeans = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
originBeans.add(new OriginBean());
targetBeans.add(new TargetBean());
}
for (int i = 0; i < 1000; i++) {
OriginBean originBean = originBeans.get(i);
TargetBean targetBean = targetBeans.get(i);
BeanUtils.copyProperties(originBean, targetBean);
}
}
/**
* 测试单个对象属性拷贝
*/
public static void copySingle(){
OriginBean originBean = new OriginBean();
TargetBean targetBean = new TargetBean();
long start = System.nanoTime();
BeanUtils.copyProperties(originBean, targetBean);
long end = System.nanoTime() - start;
System.out.println(new BigDecimal(end).divide(new BigDecimal(1000000), 6, RoundingMode.HALF_UP) + "[ms]");
}
/**
* 测试列表对象属性拷贝
* @param times
*/
public static void copyList(int times) {
List<OriginBean> originBeans = new ArrayList<>();
List<TargetBean> targetBeans = new ArrayList<>();
for (int i = 0; i < times; i++) {
originBeans.add(new OriginBean());
targetBeans.add(new TargetBean());
}
long start = System.nanoTime();
for (int i = 0; i < times; i++) {
OriginBean originBean = originBeans.get(i);
TargetBean targetBean = targetBeans.get(i);
BeanUtils.copyProperties(originBean, targetBean);
}
long end = System.nanoTime() - start;
System.out.println(new BigDecimal(end).divide(new BigDecimal(1000000), 6, RoundingMode.HALF_UP) + "[ms]");
System.out.println(new BigDecimal(end/times).divide(new BigDecimal(1000000), 6, RoundingMode.HALF_UP) + "[ms]");
}
public static void main(String[] args) {
preheat();
copySingle();
copyList(1000 * 1000);
}
}
- Cglib BeanCopier
import org.springframework.cglib.beans.BeanCopier;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
public class BeanCopierUtil {
private static final BeanCopier copier = BeanCopier.create(OriginBean.class, TargetBean.class, false);
/**
* 预热
*/
public static void preheat() {
List<OriginBean> originBeans = new ArrayList<>();
List<TargetBean> targetBeans = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
originBeans.add(new OriginBean());
targetBeans.add(new TargetBean());
}
for (int i = 0; i < 1000; i++) {
OriginBean originBean = originBeans.get(i);
TargetBean targetBean = targetBeans.get(i);
copier.copy(originBean, targetBean, null);
}
}
/**
* 测试单个对象属性拷贝
*/
public static void copySingle(){
OriginBean originBean = new OriginBean();
TargetBean targetBean = new TargetBean();
long start = System.nanoTime();
copier.copy(originBean, targetBean, null);
long end = System.nanoTime() - start;
System.out.println(new BigDecimal(end).divide(new BigDecimal(1000000), 6, RoundingMode.HALF_UP) + "[ms]");
}
/**
* 测试列表对象属性拷贝
* @param times
*/
public static void copyList(int times) {
List<OriginBean> originBeans = new ArrayList<>();
List<TargetBean> targetBeans = new ArrayList<>();
for (int i = 0; i < times; i++) {
originBeans.add(new OriginBean());
targetBeans.add(new TargetBean());
}
long start = System.nanoTime();
for (int i = 0; i < times; i++) {
OriginBean originBean = originBeans.get(i);
TargetBean targetBean = targetBeans.get(i);
copier.copy(originBean, targetBean, null);
}
long end = System.nanoTime() - start;
System.out.println(new BigDecimal(end).divide(new BigDecimal(1000000), 6, RoundingMode.HALF_UP) + "[ms]");
System.out.println(new BigDecimal(end/times).divide(new BigDecimal(1000000), 6, RoundingMode.HALF_UP) + "[ms]");
}
public static void main(String[] args) {
preheat();
copySingle();
copyList(1000 * 1000);
}
}