主流屬性拷貝工具速度比較

摘要

本文介紹四種主流的屬性拷貝工具:

  1. PropertyUtils (commons beanutils)
  2. BeanUtils (commons beanutils)
  3. BeanUtils (Spring beans)
  4. BeanCopier (cglib)

它們的屬性拷貝都基于java內(nèi)省,拷貝的對象類型必須提供setter/getter;

經(jīng)過拷貝速度測試:

  1. getter/setter 速度最快,但是不適用于數(shù)量多的屬性拷貝;
  2. BeanCopier 速度位居第二,在使用緩存的情況下,甚至與getter/setter相差無幾;
  3. Spring BeanUtils 在次數(shù)增加的情況下,速度較好,在次數(shù)少時,比commons beanutils的兩種工具都要慢;
  4. PropertyUtils 比BeanUtils 速度快,更穩(wěn)定。

一、屬性拷貝

屬性拷貝

日常開發(fā)中,經(jīng)常會有將一個java bean的屬性拷貝到另一個java bean的需求。這兩個bean可能是相同類型;也可能是不同類型,但是有部分相同字段。

最直觀的做法是使用setter/getter:

objA.setName(objB.getName());

這種方法效率是最高的;但是當(dāng)要拷貝的屬性較多時,setter/getter就不方便,不優(yōu)雅了。

有需求就會有相應(yīng)的解決方案,以下四種主流工具提供了屬性拷貝功能:

  1. org.apache.commons.beanutils.PropertyUtils#copyProperties()
  2. org.apache.commons.beanutils.BeanUtils#copyProperties()
  3. org.springframework.beans.BeanUtils#copyProperties()
  4. net.sf.cglib.beans.BeanCopier

依賴

org.apache.commons.beanutils.PropertyUtils.copyProperties以及org.apache.commons.beanutils.BeanUtils.copyProperties都由commons-beanutils提供:

<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.3</version>
</dependency>

org.springframework.beans.BeanUtils.copyProperties由spring-beans提供:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.1.3.RELEASE</version>
</dependency>

net.sf.cglib.beans.BeanCopier由cglib提供:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.7</version>
</dependency>

使用方法

屬性拷貝工具的使用方法都比較簡單,除了BeanCopier外,其他三個類都提供了靜態(tài)方法直接調(diào)用。

cglib的net.sf.cglib.beans.BeanCopier稍微麻煩一點,需要先創(chuàng)建copier:

BeanCopier beanCopier = net.sf.cglib.beans.BeanCopier.create(SourceBean.class, TargetBean.class, false);
beanCopier.copy(from, to, null);

四種工具的屬性拷貝原理,都是利用了java內(nèi)省機制;所以,需要拷貝的java bean類型都必須提供getter以及setter方法,否者拷貝失敗。

同名不同類型

拷貝時,遇到同名屬性,但是類型不同,幾種工具表現(xiàn)也不同:

org.apache.commons.beanutils.PropertyUtils#copyProperties() (同名setter,類型不同,異常)
org.apache.commons.beanutils.BeanUtils#copyProperties() (同名setter,即使類型不同也能轉(zhuǎn)換,但是值可能不正確)
org.springframework.beans.BeanUtils#copyProperties() (同名setter,即使類型不同也能轉(zhuǎn)換,但是值可能不正確)
net.sf.cglib.beans.BeanCopier(同名setter,即使類型不同也能轉(zhuǎn)換,但是值可能不正確)

二、速度比較

經(jīng)過代碼測試, 10次,1000次,1000000次各種手段的速度表現(xiàn)如下:

10次測試 第一次 第二次 第三次 平均值 每次平均值
PropertyUtils.copyProperties 0 1 0 1/3 0.033
BeanUtils.copyProperties 2 3 2 2.33 0.233
SPRING copyProperties 0 0 0 0 0
BeanCopier(without cache) 0 0 0 0 0
BeanCopier 0 0 0 0 0
getter/setter 0 0 0 0 0
1000次測試 第一次 第二次 第三次 平均值 每次平均值
PropertyUtils.copyProperties 22 21 16 19.66 0.01966
BeanUtils.copyProperties 19 29 20 22.66 0.02266
SPRING copyProperties 7 5 5 5.66 0.00566
BeanCopier(without cache) 4 4 3 3.66 0.00366
BeanCopier 0 0 1 0.33 0.00033
getter/setter 0 0 0 0 0
1000000次測試 第一次 第二次 第三次 平均值 每次平均值
PropertyUtils.copyProperties 1798 1770 1804 1767.33 0.00176733
BeanUtils.copyProperties 2904 2937 2860 2900.33 0.00290033
SPRING copyProperties 214 206 199 206.33 0.00020633
BeanCopier(without cache) 92 82 89 87.66 0.00008766
BeanCopier 8 7 8 7.66 0.00000766
getter/setter 8 7 7 7.33 0.00000733

可以得出結(jié)論:

  1. getter/setter 速度最快,但是不適用于數(shù)量多的屬性拷貝;
  2. BeanCopier 速度位居第二,在使用緩存的情況下,甚至與getter/setter相差無幾;即使不使用緩存,速度也很快;
  3. Spring BeanUtils 在次數(shù)增加的情況下,速度較好,在次數(shù)少時,比commons beanutils的兩種工具都要慢;
  4. PropertyUtils 比BeanUtils 速度快,更穩(wěn)定。

需要注意一點,BeanCopier#create開銷比較大,所以不要頻繁創(chuàng)建beanCopier實體,最使用緩存。

測試速度的代碼:

public class BeanCopyCompareTest {

    private static BeanCopier beanCopier = net.sf.cglib.beans.BeanCopier.create(SourceBean.class, TargetBean.class, false);

    public static void main(String[] args) throws Exception {

        int[] times = {1, 10, 1000, 1000000};

        for (int time : times) {
            doCopy(time, (from, to) -> org.apache.commons.beanutils.PropertyUtils.copyProperties(to, from),
                    "org.apache.commons.beanutils.PropertyUtils.copyProperties");
            doCopy(time, (from, to) -> org.apache.commons.beanutils.BeanUtils.copyProperties(to, from),
                    "org.apache.commons.beanutils.BeanUtils.copyProperties");
            doCopy(time, (BeanUtils::copyProperties),
                    "org.springframework.beans.BeanUtils.copyProperties");
            doCopy(time, ((from, to) -> beanCopier.copy(from, to, null)), "net.sf.cglib.beans.BeanCopier");
            doCopy(time, ((from, to) -> {
                BeanCopier beanCopier = net.sf.cglib.beans.BeanCopier.create(SourceBean.class, TargetBean.class, false);
                beanCopier.copy(from, to, null);
            }), "net.sf.cglib.beans.BeanCopier(without cache)");
            doCopy(time, ((from, to) -> {
                to.setAge(from.getAge());
                to.setGender(from.getGender());
                to.setName(from.getName());
                to.setRight(from.getRight());
            }), "getter/setter");

            System.out.println("-----------------------------------------------------");
        }

    }

    static void doCopy(Integer time, BeanCopy beanCopy, String type) throws Exception {
        long startTime = System.currentTimeMillis();
        for (int j = 0; j < time; j++) {
            SourceBean sourceBean = new SourceBean();
            sourceBean.setName("Richard");
            sourceBean.setGender('F');
            sourceBean.setAge(20);
            sourceBean.setRight(true);

            TargetBean targetBean = new TargetBean();
            beanCopy.copy(sourceBean, targetBean);
        }
        System.out.printf("執(zhí)行%d次用時%dms---------%s%n", time, System.currentTimeMillis() - startTime, type);
    }

    interface BeanCopy {
        void copy(SourceBean from, TargetBean to) throws Exception;
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容