Java switch 为什么比 for 循环快?

2022-08-03 15:18:51 +08:00
 cpalead
测试代码
for (int j = 0; j < 30; j++) {
long current = System.currentTimeMillis();
for (long i = 0; i < 1000000000; i++) {
getPriority(10);
}
long first = System.currentTimeMillis();
for (long i = 0; i < 1000000000; i++) {
getPriority1(10);
}
long second = System.currentTimeMillis();
System.out.println("first= " + (first - current) + " second= " + (second - first));
}

原方法
private static int getPriority(int cameraType) {
switch (cameraType) {
case 4:
return CameraType.CAMERA_1.getPriority();
case 5:
return CameraType.CAMERA_2.getPriority();
case 6:
return CameraType.CAMERA_3.getPriority();
case 7:
return CameraType.CAMERA_4.getPriority();
case 8:
return CameraType.CAMERA_5.getPriority();
case 9:
return CameraType.CAMERA_6.getPriority();
case 10:
return CameraType.CAMERA_7.getPriority();
default:
return -1;
}
}

private static int getPriority1(int cameraType) {
for (CameraType cameraType1 : CameraType.values()) {
if (cameraType1.getCameraType() == cameraType) {
return cameraType1.getPriority();
}
}
return -1;
}

所用枚举类型
public enum CameraType {
CAMERA_1("相机 1", 4, 0), CAMERA_2("相机 2", 5, 2), CAMERA_3("相机 3", 6, 3), CAMERA_4("相机 4", 7, 4),
CAMERA_5("相机 5", 8, 5), CAMERA_6("相机 6", 9, 6), CAMERA_7("相机 7", 10, 1);
private final String mCameraName;
private final int mCameraType;
private final int mPriority;

CameraType(String name, int cameraType, int priority) {
mCameraName = name;
mCameraType = cameraType;
mPriority = priority;
}

public String getCameraName() {
return mCameraName;
}

public int getCameraType() {
return mCameraType;
}

public int getPriority() {
return mPriority;
}
}

结果
first= 500 second= 8683
first= 536 second= 6801
first= 520 second= 7329
first= 639 second= 7556
first= 562 second= 7279
first= 592 second= 7458

个人感觉,switch 不还是从最开始 case 匹配到最终符合的 case 然后返回的吗?那不就跟 for 循环的匹配办法一样吗!为什么两者速度差一个数量级
3769 次点击
所在节点    Java
17 条回复
mxT52CRuqR6o5
2022-08-03 15:21:19 +08:00
你这里的 switch 可以优化成查表
xujinkai
2022-08-03 15:21:53 +08:00
我记得 switch 会优化成数组取下表,这样就不用一个一个匹配了
Leviathann
2022-08-03 15:27:39 +08:00
不要用测量前后时间的方式测计算密集的代码
lmshl
2022-08-03 15:38:00 +08:00
不考虑 JIT 介入的前提下:

# for of
1. 循环 Array 会带来检查数组边界的开销,每次访问元素都要 check condition
2. getCameraType 是函数,函数需要跳转过去,再跳转回来
3. 发生了额外的三次内存访问,a) 堆获取枚举静态数组, b) 根据数组下标访问枚举对象引用, c) 根据对象引用访问堆内存地址取属性

# switch
而 switch 只有简简单单的 compare 和 jump ,高下立判

不是很懂 jvm ,仅根据操作系统印象胡诌几句
786375312123
2022-08-03 16:07:28 +08:00
一个是 map 查表,一个是 array 遍历
xtreme1
2022-08-03 16:14:52 +08:00
cpalead
2022-08-03 17:04:25 +08:00
@xtreme1 我自己反编译过,跟你这个是类似,能看出啥?原因是啥?
cpalead
2022-08-03 17:06:51 +08:00
@Leviathann 为什么?有什么问题,不这样计算时间,怎么算时间
aguesuka
2022-08-03 17:55:06 +08:00
@cpalead 用 benchmark, 至于为什么可以谷歌一下
zmal
2022-08-03 18:09:22 +08:00
反编译后的字节码很清晰了:编译优化成查表后把枚举类进行了标量替换,tableswitch 里的数字应该是直接放在方法栈里,避免了去堆里访问对象,所以快了很多。
L4Linux
2022-08-03 18:10:19 +08:00
Java 的性能不是这么比的
L4Linux
2022-08-03 18:12:17 +08:00
还有,挨个比数肯定慢啊。。。
shyling
2022-08-03 18:15:26 +08:00
可以研究下什么是 tableswitch
superrichman
2022-08-03 18:19:34 +08:00
知道门牌号直接上门查水表比一家一户敲门快 🐶
cpalead
2022-08-03 18:44:04 +08:00
已经研究明白了
如果 case 的值是连续的,那么就会优化成一个数组,效率和原理跟 hashmap 或者数组下标访问的原理一样,那就是 o(1)的时间,如果不是连续的,就会使用二分查找去匹配,那么时间复杂度就是 o(log(n))
而 for 的时间复杂度就是 n ,所以不如 switch 快!
zagfai
2022-08-04 00:35:01 +08:00
@cpalead hash 是 o(alpha(n)), 不是 lgn
ibinary
2022-08-04 10:57:22 +08:00
遇到语法不懂得层面,就去 C++ 看看反汇编. 语法一样.看看人家底层怎么做得. switch 优化方式有很多种. 可以优化为 if 可以优化为表. 可以优化为树. 可以优化为二级查表.

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/870434

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX