各类对象属性拷贝工具性能测试对比(BeanCopier、BeanUtils、DozerBeanMapper、PropertyUtils)

说明

在工作了许久之后,也认识了很多对象属性拷贝工具,诸如标题所示的那些工具,于是就想看看哪个工具在对象拷贝方面性能更高,于是乎就有了这篇博文,希望对小伙伴们有点帮助。
由于是测试性能,代码写的比较露骨简单且基本相同,在每个工具类正式测试前,都做了代码预热,为了就是让结果尽量准确,各个工具的测试代码将贴在文末,下面直接附上测试结果。

测试环境

  • 系统: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);
    }
}
©️2020 CSDN 皮肤主题: 像素格子 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值