<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>

          Java 淺拷貝性能比較

          共 22178字,需瀏覽 45分鐘

           ·

          2021-03-11 13:33

          公眾號(hào)關(guān)注 “GitHub今日熱榜
          設(shè)為 “星標(biāo)”,帶你挖掘更多開發(fā)神器!






          一、前言


          實(shí)際開發(fā)中,經(jīng)常會(huì)遇到對(duì)象拷貝的需求,本文就結(jié)合日常開發(fā)過程中,使用到的淺拷貝技術(shù),進(jìn)行性能比較,看看誰更強(qiáng)。


          重要:下面將會(huì)花大量篇幅,列出各種類型淺拷貝的代碼,你可以直接拖到文章末尾,看性能對(duì)比結(jié)果。然后再根據(jù)你中意的對(duì)象回過頭來看它的代碼,避免疲勞。


          首先創(chuàng)建一個(gè)用于拷貝的 Bean,如下所示:


          import lombok.AllArgsConstructor;
          import lombok.Builder;
          import lombok.Data;
          import org.apache.commons.lang3.RandomStringUtils;
          import org.apache.commons.lang3.RandomUtils;

          import java.util.Date;

          @Data
          @Builder
          public class User {
              private long id;

              private int age;

              private String name;

              private boolean isMale;

              private School school;

              private Date createDate;

              public static User mock() {
                  return User.builder()
                          .id(RandomUtils.nextLong())
                          .age(RandomUtils.nextInt())
                          .name(RandomStringUtils.randomAlphanumeric(5))
                          .isMale(RandomUtils.nextBoolean())
                          .school(new School(RandomStringUtils.randomAlphanumeric(5), RandomUtils.nextInt()))
                          .createDate(new Date())
                          .build();
              }
          }

          @AllArgsConstructor
          class School {
              private String name;

              private int code;
          }


          然后編寫一個(gè)模板類,給各個(gè)淺拷貝方法提供預(yù)熱和耗時(shí)統(tǒng)計(jì)功能:


          public abstract class BaseCopyTest {
              public List<User> prepareData(int size) {
                  List<User> list = new ArrayList<>(size);
                  IntStream.range(0, size).forEach(e -> list.add(User.mock()));
                  return list;
              }

              public User prepareOne() {
                  return User.mock();
              }

              public void testCopy(List<User> data) {
                  warnUp();

                  long startTime = System.currentTimeMillis();

                  copyLogic(data);

                  System.out.println(name() + ": " + (System.currentTimeMillis() - startTime) + "ms");
              }

              abstract void warnUp();

              abstract void copyLogic(List<User> data);

              abstract String name();
          }


          二、工具類


          首先介紹下工具類這邊,代表“工具類”參賽的選手有:


          • Apache BeanUtils——廉頗老矣

          • Spring BeanUtils——夕陽紅

          • Spring BeanCopier——三十而立

          • Spring BeanCopier + Reflectasm——身強(qiáng)力壯


          2.1 Apache BeanUtils


          Apache BeanUtils 算是一個(gè)比較古老的工具類,其自身是存在性能問題的,阿里巴巴開發(fā)手冊(cè)中也明確禁止使用該工具,本次對(duì)比仍然把它加進(jìn)來把。


          想要用它需要導(dǎo)入依賴包:


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


          public class ApacheBeanUtilsTest extends BaseCopyTest {

              @Override
              void warnUp() {
                  User source = prepareOne();
                  try {
                      User target = new User();
                      System.out.println(source);
                      BeanUtils.copyProperties(target, source);
                      System.out.println(target);
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }

              @Override
              void copyLogic(List<User> data) {
                  for(User source : data) {
                      try {
                          BeanUtils.copyProperties(new User(), source);
                      } catch (Exception e) {
                          e.printStackTrace();
                      }
                  }
              }

              @Override
              String name() {
                  return "Apache BeanUtils";
              }
          }


          2.2 Spring BeanUtils


          Spring BeanUtils 和 Apache Utils API 很像,但是在效率上比 Apache 效率更高,目前使用的人也不少。引入 spring-beans 依賴包后即可使用。


          Spring BeanUtils 的 copyProperties() 方法,第一個(gè)是源對(duì)象,第二個(gè)是目標(biāo)對(duì)象。和 Apache BeanUtils 正好相反,要注意避免踩坑。


          public class SpringBeanUtilsTest extends BaseCopyTest {

              @Override
              void warnUp() {
                  User source = prepareOne();
                  User target = new User();
                  System.out.println(source);
                  BeanUtils.copyProperties(source, target);
                  System.out.println(target);
              }

              @Override
              void copyLogic(List<User> data) {
                  for(User source : data) {
                      BeanUtils.copyProperties(source, new User());
                  }
              }

              @Override
              String name() {
                  return "Spring BeanUtils";
              }
          }


          2.3 Spring BeanCopier


          Spring 還為我們提供了一種基于 Cglib 的淺拷貝方式 BeanCopier,引入 spring-core 依賴包后即可使用,它被認(rèn)為是取代 BeanUtils 的存在。


          讓我們編寫一個(gè)工具類來使用 BeanCopier,如下所示:


          public class BeanCopierUtils {
              private static final Map<String, BeanCopier> CACHE = new ConcurrentHashMap<>();

              public static void copyProperties(Object source, Object target) {
                  BeanCopier copier = getBeanCopier(source.getClass(), target.getClass());
                  copier.copy(source, target, null);
              }

              private static BeanCopier getBeanCopier(Class<?> sourceClazz, Class<?> targetClazz) {
                  String key = generatorKey(sourceClazz, targetClazz);
                  BeanCopier copier;
                  if(CACHE.containsKey(key)) {
                      copier = CACHE.get(key);
                  } else {
                      copier = BeanCopier.create(sourceClazz, targetClazz, false);
                      CACHE.put(key, copier);
                  }
                  return copier;
              }

              private static String generatorKey(Class<?> sourceClazz, Class<?> targetClazz) {
                  return sourceClazz + "_" + targetClazz;
              }
          }


          對(duì)應(yīng)的,編寫下它的測(cè)試類:


          public class BeanCopierUtilsTest extends BaseCopyTest {

              @Override
              void warnUp() {
                  User source = prepareOne();
                  User target = new User();
                  System.out.println(source);
                  BeanCopierUtils.copyProperties(source, target);
                  System.out.println(target);
              }

              @Override
              void copyLogic(List<User> data) {
                  for(User source : data) {
                      BeanCopierUtils.copyProperties(source, new User());
                  }
              }

              @Override
              String name() {
                  return "Spring BeanCopier";
              }
          }


          2.4 Spring BeanCopier + Reflectasm


          在大量對(duì)象拷貝過程中,new 操作往往是耗時(shí)的,Spring BeanCopier 并沒有解決 new 這個(gè)動(dòng)作。Reflectasm 是一個(gè)高性能的反射工具包,可以利用它來解決 new 步驟的耗時(shí)。使用 Reflectasm 需要引入依賴:


          <dependency>
              <groupId>com.esotericsoftware</groupId>
              <artifactId>reflectasm</artifactId>
              <version>1.11.9</version>
          </dependency>


          改造 BeanCopierUtils 類代碼后如下:


          public class BeanCopierReflectasmUtilsTest extends BaseCopyTest {

              @Override
              void warnUp() {
                  User source = prepareOne();
                  try {
                      System.out.println(source);
                      System.out.println(BeanCopierReflectasmUtils.copyProperties(source, User.class));
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }

              @Override
              void copyLogic(List<User> data) {
                  for(User source : data) {
                      User target = BeanCopierReflectasmUtils.copyProperties(source, User.class);
                  }
              }

              @Override
              String name() {
                  return "Spring BeanCopier Reflectasm";
              }
          }


          如上所示,拷貝方法通過 class 進(jìn)行反射創(chuàng)建對(duì)象,并對(duì) ConstructorAccess 進(jìn)行緩存,提高效率。編寫下它對(duì)應(yīng)的測(cè)試類:


          public class BeanCopierReflectasmUtilsTest extends BaseCopyTest {

              @Override
              void warnUp() {
                  User source = prepareOne();
                  try {
                      System.out.println(source);
                      System.out.println(BeanCopierReflectasmUtils.copyProperties(source, User.class));
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }

              @Override
              void copyLogic(List<User> data) {
                  for(User source : data) {
                      User target = BeanCopierReflectasmUtils.copyProperties(source, User.class);
                  }
              }

              @Override
              String name() {
                  return "Spring BeanCopier Reflectasm";
              }
          }


          三、原生類


          回過頭來介紹下代表 Java “原生類”參賽的選手:


          • new——祖師爺

          • clone——瘦死的駱駝比馬大


          3.1 new


          咱們 java 面向?qū)ο缶幊虒W(xué)習(xí)的第一個(gè)關(guān)鍵字,非 new 莫屬了。雖然淺拷貝用 new 未免太過于傻瓜,但還是把它請(qǐng)出來,看看它的性能咋樣。


          public class NewTest extends BaseCopyTest {

              @Override
              void warnUp() {
                  User source = prepareOne();
                  try {
                      User target = new User();
                      System.out.println(source);
                      target.setId(source.getId());
                      target.setAge(source.getAge());
                      target.setName(source.getName());
                      target.setMale(source.isMale());
                      target.setSchool(source.getSchool());
                      target.setCreateDate(source.getCreateDate());
                      System.out.println(target);
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }

              @Override
              void copyLogic(List<User> data) {
                  for(User source : data) {
                      User target = new User();
                      target.setId(source.getId());
                      target.setAge(source.getAge());
                      target.setName(source.getName());
                      target.setMale(source.isMale());
                      target.setSchool(source.getSchool());
                      target.setCreateDate(source.getCreateDate());
                  }
              }

              @Override
              String name() {
                  return "Java New";
              }
          }


          3.2 clone


          clone 也是 Java 原生提供的拷貝方法,并且據(jù)說性能還不錯(cuò),我司項(xiàng)目里面就還有許多用 clone 的實(shí)現(xiàn)。咱們也拉出來比劃比劃:


          使用 clone 咱們得先讓對(duì)象實(shí)現(xiàn) Cloneable 接口,修改 User:


          @Data
          @Builder
          @NoArgsConstructor
          @AllArgsConstructor
          public class User implements Cloneable {
              private long id;

              private int age;

              private String name;

              private boolean isMale;

              private School school;

              private Date createDate;

              public static User mock() {
                  return User.builder()
                          .id(RandomUtils.nextLong())
                          .age(RandomUtils.nextInt())
                          .name(RandomStringUtils.randomAlphanumeric(5))
                          .isMale(RandomUtils.nextBoolean())
                          .school(new School(RandomStringUtils.randomAlphanumeric(5), RandomUtils.nextInt()))
                          .createDate(new Date())
                          .build();
              }

              @Override
              public Object clone() {
                  try {
                      return super.clone();
                  } catch (CloneNotSupportedException e) {
                      e.printStackTrace();
                  }
                  return null;
              }
          }

          @AllArgsConstructor
          class School {
              private String name;

              private int code;
          }


          四、Lombok


          最后咱們?cè)蹃斫榻B下 Lombok 的淺拷貝,代表 Lombok 出場(chǎng)的有兩位選手:


          • toBuilder——后起之秀

          • newBuilder——迅雷不及掩耳之勢(shì)


          4.1 toBuilder


          想要開啟 Lombok 的 toBuilder 功能,需要將 User 類上方的 @Builder 修改為 @Builder(toBuilder = true) 即可,編寫它的測(cè)試類:


          public class ToBuilderTest extends BaseCopyTest {

              @Override
              void warnUp() {
                  User source = prepareOne();
                  System.out.println(source);
                  System.out.println(source.toBuilder().build());
              }

              @Override
              void copyLogic(List<User> data) {
                  for(User source : data) {
                      User target = source.toBuilder().build();
                  }
              }

              @Override
              String name() {
                  return "Lombok toBuilder";
              }
          }


          4.2 newBuilder


          再來介紹下 Lombok 的 newBuilder,它有點(diǎn)類似于 new,有點(diǎn)傻瓜,但也把它列出來,看看性能咋樣:


          public class NewBuilderTest extends BaseCopyTest {

              @Override
              void warnUp() {
                  User source = prepareOne();
                  System.out.println(source);
                  System.out.println(this.copy(source));
              }

              @Override
              void copyLogic(List<User> data) {
                  for(User source : data) {
                      User target = this.copy(source);
                  }
              }

              private User copy(User source) {
                  return User.builder()
                          .id(source.getId())
                          .age(source.getAge())
                          .name(source.getName())
                          .isMale(source.isMale())
                          .school(source.getSchool())
                          .createDate(source.getCreateDate())
                          .build();
              }

              @Override
              String name() {
                  return "Lombok newBuilder";
              }
          }


          五、測(cè)試


          經(jīng)過漫長的選手出場(chǎng)介紹,咱們終于可以進(jìn)行性能對(duì)比了。首先介紹下本機(jī)器配置信息:


          • Win10 專業(yè)版 1909

          • AMD Ryzen 5 3600 6-Core

          • 16GB RAM


          測(cè)試均采用單線程測(cè)試,壓測(cè)不同數(shù)據(jù)量情況下各種方式的耗時(shí)結(jié)果,測(cè)試結(jié)果如下(單位ms)。




          排除掉 BeanUtils 后,結(jié)果如下:



          最后簡單總結(jié)下:


          1. 禁止使用 Apache BeanUtils,性能差到離譜

          2. 不推薦使用 Spring BeanUtils,可以使用 Spring BeanCopier 替代

          3. Spring BeanCopier Reflectasm 和 Spring BeanCopier 相比提升不了性能,但是寫起來更簡便(不需要顯式 new 對(duì)象)

          4. Java 原生的 new 和 clone 性能很高,可以使用 clone

          5. Lombok 的 toBuilder 速度也很快,并且寫起來很方便,推薦使用



          作者: Jitwxs

          鏈接: https://jitwxs.cn/a9fa88a0.html







          關(guān)注GitHub今日熱榜,專注挖掘好用的開發(fā)工具,致力于分享優(yōu)質(zhì)高效的工具、資源、插件等,助力開發(fā)者成長!







          點(diǎn)個(gè)在看 你最好看









          瀏覽 45
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  久久精品国产亚洲AV成人婷婷 | 久久久久久无码精品人妻一区蜜桃影院 | 国产精品少大保健 | 欧美人操逼 | 狼网久久 |