Java的几种创建实例方法的性能对比(二)

上一篇里对几种书写方式进行了简单的测试,得出了一些初步的结论。这次简单了解Lambda原理后,对测试做了一些调整,发现得到不一样的结果,而这个调整,明显更契合实际开发的场景。

暂时还没有亲自去验证,主要是从博客中了解的Lambda原理,引起一些启发,对测试代码进行了一些改善。

在此感谢这篇博客的作者:https://www.cnblogs.com/UncleWang001/p/10020611.html - lambda表达式底层处理机制

调整后的代码:

  1 package com.supalle.test;
  2 
  3 import lombok.AllArgsConstructor;
  4 import lombok.Builder;
  5 import lombok.Data;
  6 import lombok.NoArgsConstructor;
  7 
  8 import java.lang.reflect.Constructor;
  9 import java.lang.reflect.InvocationTargetException;
 10 import java.util.function.Supplier;
 11 
 12 /**
 13  * @描述:语法PK
 14  * @作者:Supalle
 15  * @时间:2019/7/26
 16  */
 17 public class SyntaxPKTest {
 18 
 19 
 20     /* 循环次数 */
 21     private final static int SIZE = 100000000;
 22 
 23     /* 有类如下 */
 24     @Data
 25     @Builder
 26     @NoArgsConstructor
 27     @AllArgsConstructor
 28     private static class Man {
 29         private String name;
 30         private int age;
 31     }
 32 
 33 
 34     /**
 35      * 使用 new Man();
 36      *
 37      * @return 运行耗时
 38      */
 39     public static long runWithNewConstructor() {
 40         long start = System.currentTimeMillis();
 41 
 42         for (int i = 0; i < SIZE; i++) {
 43             new SyntaxPKTest.Man();
 44         }
 45 
 46         return System.currentTimeMillis() - start;
 47     }
 48 
 49     /**
 50      * 使用反射
 51      *
 52      * @return 运行耗时
 53      */
 54     public static long runWithReflex() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
 55         Constructor<SyntaxPKTest.Man> constructor = SyntaxPKTest.Man.class.getConstructor();
 56         long start = System.currentTimeMillis();
 57 
 58         for (int i = 0; i < SIZE; i++) {
 59             constructor.newInstance();
 60         }
 61 
 62         return System.currentTimeMillis() - start;
 63     }
 64 
 65     /**
 66      * 使用内部类调用 new Man();
 67      *
 68      * @return 运行耗时
 69      */
 70     public static long runWithSubClass() {
 71         long start = System.currentTimeMillis();
 72 
 73         Supplier<Man> supplier = new Supplier<Man>() {
 74             @Override
 75             public Man get() {
 76                 return new Man();
 77             }
 78         };
 79         for (int i = 0; i < SIZE; i++) {
 80             supplier.get();
 81 
 82         }
 83 
 84         return System.currentTimeMillis() - start;
 85     }
 86 
 87     /**
 88      * 使用Lambda调用 new Man();
 89      *
 90      * @return 运行耗时
 91      */
 92     public static long runWithLambda() {
 93         long start = System.currentTimeMillis();
 94 
 95         Supplier<Man> supplier = () -> new Man();
 96 
 97         for (int i = 0; i < SIZE; i++) {
 98             supplier.get();
 99         }
100 
101         return System.currentTimeMillis() - start;
102     }
103 
104 
105     /**
106      * 使用 MethodReference
107      *
108      * @return 运行耗时
109      */
110     public static long runWithMethodReference() {
111         long start = System.currentTimeMillis();
112 
113         Supplier<Man> supplier = Man::new;
114 
115         for (int i = 0; i < SIZE; i++) {
116             supplier.get();
117         }
118 
119         return System.currentTimeMillis() - start;
120     }
121 
122     public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
123 
124         // 测试前调用一下,加载Man字节码,尽量公平
125         SyntaxPKTest.Man man1 = new SyntaxPKTest.Man();
126         SyntaxPKTest.Man man2 = new SyntaxPKTest.Man("张三", 20);
127 
128         System.out.println("测试环境:CPU核心数 - " + Runtime.getRuntime().availableProcessors());
129 
130         System.out.println();
131 
132         // 这里的话对比再次调用的时间
133         System.out.println("首次使用 new Man()            耗时:" + runWithNewConstructor());
134         System.err.println("再次使用 new Man()            耗时:" + runWithNewConstructor());
135         System.out.println("首次使用反射                   耗时:" + runWithReflex());
136         System.err.println("再次使用反射                   耗时:" + runWithReflex());
137         System.out.println("首次使用内部类调用 new Man()    耗时:" + runWithSubClass());
138         System.err.println("再次使用内部类调用 new Man()    耗时:" + runWithSubClass());
139         System.out.println("首次使用Lambda调用 new Man()   耗时:" + runWithLambda());
140         System.err.println("再次使用Lambda调用 new Man()   耗时:" + runWithLambda());
141         System.out.println("首次使用 MethodReference      耗时:" + runWithMethodReference());
142         System.err.println("再次使用 MethodReference      耗时:" + runWithMethodReference());
143 
144 
145     }
146 
147 }

这次调整,仅仅只是把内部类、Lambda、Method Reference 放到循环外边,更符合我们常用的情形。

测试结果较之上一篇真的有很大的变化。

 1 首次使用 new Man()            耗时:4
 2 再次使用 new Man()            耗时:1
 3 首次使用反射                   耗时:237
 4 再次使用反射                   耗时:251
 5 首次使用内部类调用 new Man()    耗时:5
 6 再次使用内部类调用 new Man()    耗时:2
 7 首次使用Lambda调用 new Man()   耗时:41
 8 再次使用Lambda调用 new Man()   耗时:3
 9 首次使用 MethodReference      耗时:4
10 再次使用 MethodReference      耗时:1

 

 1 首次使用 new Man()            耗时:3
 2 再次使用 new Man()            耗时:2
 3 首次使用反射                   耗时:240
 4 再次使用反射                   耗时:256
 5 首次使用内部类调用 new Man()    耗时:5
 6 再次使用内部类调用 new Man()    耗时:3
 7 首次使用Lambda调用 new Man()   耗时:43
 8 再次使用Lambda调用 new Man()   耗时:4
 9 首次使用 MethodReference      耗时:4
10 再次使用 MethodReference      耗时:1

 

首次使用 new Man()            耗时:3
再次使用 new Man()            耗时:2
首次使用反射                   耗时:238
再次使用反射                   耗时:251
首次使用内部类调用 new Man()    耗时:5
再次使用内部类调用 new Man()    耗时:1
首次使用Lambda调用 new Man()   耗时:39
再次使用Lambda调用 new Man()   耗时:5
首次使用 MethodReference      耗时:3
再次使用 MethodReference      耗时:2

 

可以看到Lambda和Method Reference同样耗时非常小,主要是在使用其创建了Supplier<Man>接口的实现实例后,本质上就和内部类创建出来的对象没有差别,因此在性能表现上相差不大。

结论:

  如果既要保持灵活简洁又追求极致的性能时,那么使用在Lambda或者Method Reference时,尽量不要写在循环内部。

 

posted @ 2019-08-10 18:23  Supalle  阅读(264)  评论(0编辑  收藏  举报