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时,尽量不要写在循环内部。
Java的几种创建实例方法的性能对比(二)的更多相关文章
- Java的几种创建实例方法的性能对比
近来打算自己封装一个比较方便读写的Office Excel 工具类,前面已经写了一些,比较粗糙本就计划重构一下,刚好公司的电商APP后台原有的导出Excel实现出现了可怕的性能问题,600行的数据生成 ...
- java讲讲几种常见的排序算法(二)
java讲讲几种常见的排序算法(二) 目录 java讲讲几种常见的排序算法(一) java讲讲几种常见的排序算法(二) 堆排序 思路:构建一个小顶堆,小顶堆就是棵二叉树,他的左右孩子均大于他的根节点( ...
- Java中两种实现多线程方式的对比分析
本文转载自:http://www.linuxidc.com/Linux/2013-12/93690.htm#0-tsina-1-14812-397232819ff9a47a7b7e80a40613cf ...
- Go_18: Golang 中三种读取文件发放性能对比
Golang 中读取文件大概有三种方法,分别为: 1. 通过原生态 io 包中的 read 方法进行读取 2. 通过 io/ioutil 包提供的 read 方法进行读取 3. 通过 bufio 包提 ...
- Golang 中三种读取文件发放性能对比
Golang 中读取文件大概有三种方法,分别为: 1. 通过原生态 io 包中的 read 方法进行读取 2. 通过 io/ioutil 包提供的 read 方法进行读取 3. 通过 bufio 包提 ...
- 求斐波那契数列第n位的几种实现方式及性能对比(c#语言)
在每一种编程语言里,斐波那契数列的计算方式都是一个经典的话题.它可能有很多种计算方式,例如:递归.迭代.数学公式.哪种算法最容易理解,哪种算法是性能最好的呢? 这里给大家分享一下我对它的研究和总结:下 ...
- java线程——三种创建线程的方式
前言 线程,英文Thread.在java中,创建线程的方式有三种: 1.Thread 2.Runnable 3.Callable 在详细介绍下这几种方式之前,我们先来看下Thread类和Runnabl ...
- Java数组3种创建方式
public static void main(String[] args){ /** * 1. 固定大小的空数组, 动态创建 */ String[] strArr1 = new String[3]; ...
- Delegate、Thread、Task、ThreadPool几种方式创建异步任务性能对比
开始预测的结果是 Task>Delegate>ThreadPool>>Thread. (一)测试代码 static async Task<int> AsyncTas ...
随机推荐
- kmp算法 汇总
来源:http://blog.csdn.net/qq_34494458/article/details/75253466 KMP算法,是由Knuth,Morris,Pratt共同提出的模式匹配算法,其 ...
- Sqlite3:Sqlite3命令行Linux操作
1.查看sqlite版本 [istester@ietester.com idoxu]$ sqlite3 -version 2.进入sqlite后台操作 指定一个完整文件的路径名,打开或者创建数据库(文 ...
- XCTF getit
一.查壳 是linux的文件.没加壳 二.拖入ida 分析一下逻辑,发现就是t的值就是flag. 写个exp就出来了. 三.exp分享 s='c61b68366edeb7bdce3c6820314b7 ...
- c语言:scanf()高级应用
1) 指定读取长度 还记得在 printf() 中可以指定最小输出宽度吗?就是在格式控制符的中间加上一个数字,例如,%10d表示输出的整数至少占用 10 个字符的位置: 如果整数的宽度不足 10,那么 ...
- C语言的编译与链接
C语言源文件要经过编译.链接才能生成可执行程序:编译(Compile)会将源文件(.c文件)转换为目标文件.对于 VC/VS,目标文件后缀为.obj:对于GCC,目标文件后缀为.o.编译是针对单个源文 ...
- MQTT 2——服务端安装与客户端测试
本篇记录一下MQTT服务器,客户端,JAVA客户端的选择开发与测试 MQTT服务端选择与安装过程:MQTT客户端测试工具安装与测试:MQTT JAVA客户端的选择与开发,测试 MQTT服务器选择与安装 ...
- groff编写man页
groff 是大多数 Unix 系统上所提供的流行的文本格式化工具 nroff/troff 的 GNU 版本.它一般用于编写手册页,即命令.编程接口等的在线文档.在本文中,我们将给你展示如何使用 gr ...
- VSCode 如何远程连接其他主机的 WSL2
VSCode 如何远程连接其他主机的 WSL2 VSCode 的 Remote Deployment 插件对 WSL2 直接提供了支持,能够很方便的连接本机的 WSL2 ,但是并没有提供一个连接远程 ...
- 简单快速安装Apache+PHP+MySql服务环境(二)—— centos使用yum安装php5.6
使用默认方式yum install php安装的php版本为5.4,在使用某些PHP框架的时候可能会有问题,所以需要安装高版本的PHP. 通过yum list php*查看是否有自己需要安装的版本,如 ...
- map 和 unordered_map
map就是映射. 定义 map<typename,typename> 注:map的元素是pair. 特性 map会对第一个对象自动排序. map不允许有两个相同的关键字. map可以定义迭 ...