<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          幾款VO數(shù)據(jù)轉(zhuǎn)換工具性能剖析

          共 945字,需瀏覽 2分鐘

           ·

          2021-12-03 18:33


          前言

          做后端開發(fā)的各位小伙伴應(yīng)該對數(shù)據(jù)轉(zhuǎn)換都不陌生,在實際開發(fā)中也肯定遇到過各種數(shù)據(jù)類型之間的轉(zhuǎn)換,但是在數(shù)據(jù)轉(zhuǎn)換的時候,往往是需要考慮性能問題的,特別是在大批量數(shù)據(jù)轉(zhuǎn)換的時候更是如此,所以本著性能為王的原則,我們今天來評測幾種數(shù)據(jù)轉(zhuǎn)換方案,主要包括以下幾種:

          • getter/setter手動賦值
          • BeanUtils
          • BeanMap
          • 反射

          我們先說結(jié)論,這幾種方式性能排行依次是:getter/setter > 反射 > BeanMap > BeanUtil

          數(shù)據(jù)轉(zhuǎn)換

          因為參與公司的重構(gòu)項目,前段時間一直比較忙,工作重點主要是業(yè)務(wù)代碼編寫測試,最近稍微好一點,主要在做一些優(yōu)化方面的工作,由于本次項目重構(gòu)主要是為了解決性能問題,所以這次重構(gòu)對性能的要求也稍微高一點,昨天在優(yōu)化數(shù)據(jù)轉(zhuǎn)換方案的時候,在自己的探索和不屑努力之下,終于找到了另外一種性能比較好的數(shù)據(jù)轉(zhuǎn)換方案,所以今天我們的內(nèi)容就是這幾種數(shù)據(jù)轉(zhuǎn)換方案的對比。

          我們先看下兩個需要進行轉(zhuǎn)換的VO,首先是entity,類似于數(shù)據(jù)庫查出來的數(shù)據(jù):

          public?class?UserEntity?{
          ????/**
          ?????*?用戶?id
          ?????*/

          ????private?Long?id;
          ????/**
          ?????*?用戶名
          ?????*/

          ????private?String?userName;
          ????/**
          ?????*?昵稱
          ?????*/

          ????private?String?nickName;

          ???//?省略構(gòu)造方法和getter/setter方法
          ??}

          然后是另一個,類似于需要給前端返回的數(shù)據(jù):

          public?class?UserVo?{
          ????/**
          ?????*?用戶?id
          ?????*/

          ????private?Long?id;
          ????/**
          ?????*?用戶名
          ?????*/

          ????private?String?userName;
          ????/**
          ?????*?昵稱
          ?????*/

          ????private?String?nickName;
          ????//?省略構(gòu)造方法和getter/setter方法
          }

          數(shù)據(jù)初始化:

          ?//?數(shù)據(jù)初始化
          List?userEntityList?=?Lists.newArrayList();
          for?(int?i?=?0;?i?10000;?i++)?{
          ????UserEntity?userEntity?=?new?UserEntity(123L?+?i,?"syske",?"云中志");
          ????userEntityList.add(userEntity);
          }

          為了方便測試,這里我先初始10000條數(shù)據(jù)。

          getter/setter

          這種方式是最直接、性能最好的方式,當(dāng)然也是兼容性最差的方式,因為這種方式兩個對象需要強依賴,如果字段增加需要修改轉(zhuǎn)換方法,對改動不友好:

          long?start1?=?System.currentTimeMillis();
          List?userVoList?=?Lists.newArrayList();
          for?(UserEntity?userEntity?:?userEntityList)?{
          ????UserVo?userVo?=?new?UserVo();
          ????userVo.setId(userEntity.getId());
          ????userVo.setUserName(userEntity.getUserName());
          ????userVo.setNickName(userEntity.getNickName());
          ????userVoList.add(userVo);
          }
          System.out.printf("getter/setter耗時:%s\n",?System.currentTimeMillis()?-?start1);

          BeanUtils

          BeanUtilsspring-beans包下面的一個工具了,為我們提供了豐富的方法,這里我們主要測試的是copyProperties方法,這個方法有兩個參數(shù),第一個參數(shù)是源數(shù)據(jù),第二個是需要賦值的目標(biāo)數(shù)據(jù):

          ?long?start2?=?System.currentTimeMillis();
          ?List?userVoList2?=?Lists.newArrayList();
          ?for?(UserEntity?userEntity?:?userEntityList)?{
          ?UserVo?userVo?=?new?UserVo();
          ?BeanUtils.copyProperties(userEntity,?userVo);
          ?userVoList2.add(userVo);
          ?}
          ?System.out.printf("BeanUtils耗時:%s\n",?System.currentTimeMillis()?-?start2);

          這種方式就比較簡潔了,簡單來說就是對兩個對象進行屬性值拷貝,但是屬性必須要有setter方法(被賦值方)和getter方法(源數(shù)據(jù)),copyProperties方法提供了忽略屬性值的方式,除此外暫未發(fā)現(xiàn)有任何優(yōu)點。如果只是單個對象的轉(zhuǎn)換,而且不考慮性能的話,這種方式也比較方便。(起初我以為它至少可以提供無getter/setter方法的值拷貝,但是測試之后我發(fā)現(xiàn)我想多了,底層還是反射)。

          BeanMap

          BeanMap也是spring-beans包下面的,不過它屬于cglib包(一個強大的,高性能,高質(zhì)量的Code生成類庫),它主要提供了一種Bean轉(zhuǎn)Map的能力,最初我也是因為這個接觸這個包的,最后發(fā)現(xiàn)它還可以用來做類型轉(zhuǎn)換,而且性能也還不錯。通過BeanMap來實現(xiàn)類型轉(zhuǎn)換的思路也很簡單,就是分別將目標(biāo)vo和源vo分別轉(zhuǎn)為BeanMap,然后用源VoBeanMap覆蓋目標(biāo)voBeanMap,然后通過BeanMapgetBean方法從BeanMap中拿到賦值后的vo,下面是具體實現(xiàn):

          ?long?start3?=?System.currentTimeMillis();
          List?userVoList3?=?Lists.newArrayList();
          for?(UserEntity?userEntity?:?userEntityList)?{
          ????UserVo?userVo?=?new?UserVo();
          ????BeanMap?entityMap?=?BeanMap.create(userEntity);
          ????BeanMap?userVOMap?=?BeanMap.create(userVo);
          ????userVOMap.putAll(entityMap);
          ????userVoList3.add((UserVo)?userVOMap.getBean());
          }
          System.out.printf("BeanMap耗時:%s\n",?System.currentTimeMillis()?-?start3);

          這種方式的好處也是簡潔,但是性能比BeanUtils好,和getter/setter比更靈活,缺點是不能忽略值,但是可以自己實現(xiàn),也不難。

          反射

          反射這種方式也算比較原始的解耦方式,缺點是稍微有點繁瑣,但是優(yōu)勢是性能比BeanUtilsBeanMap要好。

          long?start4?=?System.currentTimeMillis();
          List?userVoList4?=?Lists.newArrayList();
          for?(UserEntity?userEntity?:?userEntityList)?{
          ????UserVo?userVo?=?new?UserVo();
          ????Class?eClass?=?userEntity.getClass();
          ????Class?vClass?=?userVo.getClass();
          ????Field[]?fields?=?eClass.getDeclaredFields();
          ????Field[]?vClassDeclaredFields?=?vClass.getDeclaredFields();
          ????List?fieldList?=??Lists.newArrayList(vClassDeclaredFields);
          ????for?(Field?field?:?fields)?{
          ????????if?(fieldList.contains(field))?{
          ????????????String?name?=?field.getName().substring(0,?1).toUpperCase()?+?field.getName().substring(1);
          ????????????Method?setter?=?vClass.getMethod("set"?+?name,?field.getType());
          ????????????Method?getter?=?eClass.getMethod("get"?+?name,?null);
          ????????????setter.invoke(userVo,?getter.invoke(userEntity,?null));
          ????????}
          ????}
          ????userVoList4.add(userVo);
          }
          System.out.printf("反射耗時:%s",?System.currentTimeMillis()?-?start4);

          但是這種方式的缺點是如果兩個vo屬性不一致時需要單獨處理,我們可以看到代碼中有屬性字段的校驗。

          性能對比

          下面我們就分別針對不同的數(shù)據(jù)量做一個簡單的測試,對比下各種方案的性能,首先是三個字段在不同數(shù)據(jù)量下的性能比較:

          47a35b15898dbe5ac94c81b5e8395c2f.webp

          三個字段進行數(shù)據(jù)轉(zhuǎn)換時,我們可以得出以下結(jié)論:

          • 在十萬條數(shù)據(jù)的數(shù)據(jù)之前,getter/setter性能變化不大,一直表現(xiàn)很優(yōu)秀,大概是BeanUtils30倍;
          • BeanUntilsBeanMap差距也不是特別大,差距最大也就兩倍左右;反射和getter/setter的性能差異大概是3倍;
          • 綜合極值來看,getter/setter的性能差異大概是50倍,性能急劇變化發(fā)生在數(shù)據(jù)由10萬變?yōu)?code style="background-color:rgba(27,31,35,.05);font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(255,100,65);">100萬的時候;反射的性能差異差不多是90倍,性能是從10000條的時候發(fā)生變化的;BeanMapBeanUtils性能變化不到,差不多7倍左右

          下面我們再看下字段數(shù)量增多的情況:

          9ae3b512ef9426a43806352e9b529a72.webp

          相比于3個字段,12個字段的性能并沒有發(fā)生太大改變,變化比較大的是反射這種方式,其他三種方式并沒有太大變化,甚至還出現(xiàn)性能更好的情況,但是再100萬數(shù)據(jù)量的時候,反射性能比BeanMap差,不過也能想明白,畢竟字段越多,反射需要循環(huán)的次數(shù)就越多,所以性能會下降。好了,關(guān)于測試我們就到這里吧。

          結(jié)語

          介于時間的關(guān)系,我們今天的內(nèi)容就先到這里,感興趣的小伙伴可以自己測試下,總體來說,我們的預(yù)期目標(biāo)算是達成了。最后,希望通過今天的性能測試,能夠讓各位小伙伴重視開發(fā)過程中的性能問題,找到更適合的方案。好了,各位小伙伴,晚安吧!

          - END -


          瀏覽 90
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美日韩18 | 黄色一级视频在线观看 | 色婷亚州五月天 | 黄色小电影网站 | 大大鸡吧轻轻操在线视频 |