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 ...
随机推荐
- 使用Dice loss实现清晰的边界检测
前言: 在深度学习和计算机视觉中,人们正在努力提取特征,为各种视觉任务输出有意义的表示.在一些任务中,我们只关注对象的几何形状,而不管颜色.纹理和照明等.这就是边界检测的作用所在. 关注公众号CV ...
- JPA事务中的异常最后不也抛出了,为什么没被catch到而导致回滚?
上周,我们通过这篇文章<为什么catch了异常,但事务还是回滚了?>来解释了,之前test4为什么会回滚的原因. 但还是收到了很多没有理解的反馈,主要是根据前文给出的线索去跟踪,是获得到了 ...
- CentOS-自定义SFTP用户及目录
ftp功能说明:通过SSH启动CentOS的sftp功能 创建用户组及用户(sftp可变) $ groupadd sftp $ useradd -g sftp -s /sbin/nologin -d ...
- mysql 去重的两种方式
1.distinct一般用于获取不重复字段的条数 使用原则: 1)distinct必须放在要查询字段的开头,不能放在查询字段的中间或者后面 select distinct name from user ...
- 南京大学计算机基础 ELF和可执行文件格式
1.可重定位目标文件格式 主要是由ELF头,一些节比如.text节,.rodata节,.data节,.bss节等,前面是只读的,后面是可读可写的,加上一个节头表 1.1 ELF头里面主要包含了16字节 ...
- Leetcode No.53 Maximum Subarray(c++实现)
1. 题目 1.1 英文题目 Given an integer array nums, find the contiguous subarray (containing at least one nu ...
- vim程序编辑器---常用操作整理
vim程序编辑器---常用操作整理 移动光标方法 o 在光标行的下一行,进入编辑模式 $ 移动到光标这行,最末尾的地方 G(大写) 移动到文件最末行 :set nu 文件显示行数 :set non ...
- sscanf功能详解(转)
#include <stdio.h> #include <stdlib.h> #include <string.h> static void sscanf_test ...
- Linux小白基础命令操作
[root@localhost ~]]# [当前登录系统的用户@主机名称 当前所在的目录]# #表示为管理员登录 $ 表示为普通用户登录 切换用户su 用户名 切换后所在目录不变 ,#变成$ ...
- C语言:编程求任意月份的天数
闰年问题,因为二月份的天数与闰年有关.闰年的判断依据是:若某年能被4整除,但不能被100整除,则这一年是闰年:若某年能被400整除,则这一年也是闰年 #include <stdio.h> ...