通常而言大家普遍的认知里switch case的效率高于if else。根据我的理解而言switch的查找类似于二叉树,if则是线性查找。按照此逻辑推理对于对比条件数目大于3时switch更优,并且对比条件数目越多时switch的优势越为明显。
一、测试目的
最近与开发同学对于前面提到的性能问题,有着各自不同的见解,为证明我的观点,现设计如下测试场景验证
PS:一个方法里多达65个if else
二、测试策略
利用Junit4执行本次测试,分别设计50个、70个、100个条件式测试,每轮测试分别执行1千万、2千万、3千万、4千万、5千万和6千万次,为了力求让每轮测试不受外部因素干扰每轮测试执行10次收集信息分析。
为了让java在纯净的环境中运行。同时关闭了QQ、360、chrome等应用软件。
三、测试环境
- Java 版本信息
Java version "1.7.0_25"
Java(TM) SE Runtime Environment (build 1.7.0_25-b17)
Java HotSpot(TM) 64-Bit Server VM (build 23.25-b01, mixed mode)
Junit4
- JVM配置信息
--launcher.XXMaxPermSize
256m
-Dosgi.requiredJavaVersion=1.6
-Xms512m
-Xmx1024m
- 系统信息
Windows7 旗舰版
64位操作系统
- 设备信息
处理器:Intel(R) Core(TM) i3-2328M CPU @ 2.20GHz 2.20 GHz
安装内存(RAM):4.00GB (3.90 GB 可用)
因此次不涉及硬盘读写,故不记录硬盘信息
四、测试脚本
因篇幅所限,测试脚本略有缩减。
1 import java.util.Calendar;
2 import java.util.GregorianCalendar;
3 import java.util.Random;
4 import org.junit.After;
5 import org.junit.Before;
6 import org.junit.Test;
7 public
class ServerServiceTest {
8 Calendar calender_begin, calender_end;
9 Long time_begin, time_end; //
记录测试开始时间,结束时间
10
int flagNumber = 1000000;
//
迭代数
11 Random r =
new Random();
12 int i =
new Random().nextInt(100);
//
生成随机种子
13 @Before
14 public
void setUp()
throws Exception {
15 calender_begin = new GregorianCalendar();
16 time_begin = calender_begin.getTimeInMillis();
17 }
18 @After
19 public
void tearDown()
throws Exception {
20 calender_end = new GregorianCalendar();
21 time_end = calender_end.getTimeInMillis();
22 System.out.println(time_end - time_begin);
23 }
24 @Test
25 public
void ifTest() {
26 for (
int temp = 0; temp < flagNumber; temp++) {
27 i = r.nextInt(100);
28 if (i == 0) {
29 } else
if (i == 1) {
30 } else
if (i == 2) {
31 } else
if (i == 3) {
32 } else
if (i == 4) {
33 } else
if (i == 5) {
34 } else
if (i == 6) {
35 } else
if (i == 7) {
36 } else
if (i == 8) {
37 } else
if (i == 9) {
38 } else
if (i == 10) {
39 }
40 }
41 }
42 @Test
43 public
void switchTest()
throws InterruptedException {
44 for (
int temp = 0; temp < flagNumber; temp++) {
45 i = r.nextInt(100);
46 switch (i) {
47 case 0:
48 break;
49 case 1:
50 break;
51 case 2:
52 break;
53 case 3:
54 break;
55 case 4:
56 break;
57 case 5:
58 break;
59 default:
60 break;
61 }
62 }
63 }
64 View Code
五、测试结果
以下是收集的测试数据, 时间单位毫秒(ms)。其实这种数据看起来很难看出问题所在。
条件式测试数 | 迭代数 | 1 (ms) | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | avg | max | min | |
100 | 6千万 | if | 469 | 466 | 474 | 455 | 477 | 478 | 466 | 460 | 464 | 483 | 469 | 483 | 455 |
switch | 443 | 443 | 441 | 438 | 443 | 437 | 441 | 442 | 439 | 438 | 441 | 443 | 437 | ||
5千万 | if | 399 | 420 | 394 | 403 | 408 | 402 | 403 | 393 | 410 | 430 | 406 | 430 | 393 | |
switch | 367 | 374 | 370 | 366 | 374 | 382 | 381 | 376 | 373 | 397 | 376 | 397 | 366 | ||
4千万 | if | 344 | 325 | 326 | 359 | 320 | 325 | 324 | 319 | 319 | 328 | 329 | 359 | 319 | |
switch | 302 | 305 | 300 | 315 | 302 | 302 | 298 | 318 | 297 | 300 | 304 | 318 | 297 | ||
3千万 | if | 255 | 249 | 240 | 248 | 249 | 247 | 250 | 256 | 251 | 246 | 249 | 256 | 240 | |
switch | 228 | 232 | 227 | 231 | 230 | 229 | 227 | 231 | 228 | 231 | 229 | 232 | 227 | ||
2千万 | if | 211 | 177 | 183 | 182 | 181 | 172 | 174 | 170 | 175 | 178 | 180 | 211 | 170 | |
switch | 165 | 149 | 155 | 152 | 154 | 155 | 155 | 166 | 151 | 158 | 156 | 166 | 149 | ||
1千万 | if | 179 | 174 | 176 | 176 | 169 | 177 | 176 | 191 | 173 | 183 | 177 | 191 | 169 | |
switch | 152 | 156 | 167 | 161 | 158 | 151 | 161 | 161 | 159 | 161 | 159 | 167 | 151 | ||
70 | 6千万 | if | 424 | 416 | 440 | 437 | 427 | 419 | 417 | 411 | 416 | 429 | 424 | 440 | 411 |
switch | 389 | 395 | 387 | 388 | 388 | 392 | 397 | 391 | 392 | 393 | 391 | 397 | 387 | ||
5千万 | if | 368 | 366 | 352 | 354 | 351 | 352 | 350 | 362 | 355 | 361 | 357 | 368 | 350 | |
switch | 327 | 327 | 326 | 324 | 328 | 327 | 324 | 323 | 330 | 325 | 326 | 330 | 323 | ||
4千万 | if | 321 | 300 | 295 | 293 | 284 | 283 | 281 | 335 | 276 | 281 | 295 | 335 | 276 | |
switch | 259 | 262 | 260 | 262 | 259 | 261 | 259 | 268 | 260 | 267 | 262 | 268 | 259 | ||
3千万 | if | 219 | 229 | 226 | 217 | 220 | 226 | 215 | 223 | 217 | 226 | 222 | 229 | 215 | |
switch | 199 | 197 | 203 | 199 | 199 | 199 | 197 | 200 | 200 | 197 | 199 | 203 | 197 | ||
2千万 | if | 149 | 158 | 152 | 155 | 177 | 159 | 159 | 158 | 161 | 150 | 158 | 177 | 149 | |
switch | 136 | 136 | 132 | 134 | 145 | 133 | 133 | 132 | 136 | 133 | 135 | 145 | 132 | ||
1千万 | if | 86 | 83 | 87 | 81 | 90 | 88 | 77 | 83 | 95 | 85 | 86 | 95 | 77 | |
switch | 65 | 67 | 67 | 67 | 68 | 71 | 67 | 68 | 68 | 68 | 68 | 71 | 65 | ||
50 | 6千万 | if | 374 | 361 | 363 | 363 | 362 | 364 | 376 | 366 | 372 | 373 | 367 | 376 | 361 |
switch | 347 | 343 | 341 | 341 | 338 | 362 | 340 | 343 | 343 | 343 | 344 | 362 | 338 | ||
5千万 | if | 324 | 312 | 306 | 306 | 341 | 312 | 312 | 299 | 307 | 307 | 313 | 341 | 299 | |
switch | 289 | 287 | 285 | 283 | 291 | 288 | 290 | 288 | 290 | 281 | 287 | 291 | 281 | ||
4千万 | if | 287 | 247 | 251 | 252 | 265 | 247 | 248 | 256 | 252 | 256 | 256 | 287 | 247 | |
switch | 239 | 237 | 236 | 229 | 243 | 230 | 235 | 232 | 228 | 228 | 234 | 243 | 228 | ||
3千万 | if | 193 | 196 | 195 | 197 | 203 | 198 | 201 | 188 | 200 | 204 | 198 | 204 | 188 | |
switch | 184 | 178 | 181 | 175 | 173 | 172 | 176 | 184 | 193 | 174 | 179 | 193 | 172 | ||
2千万 | if | 128 | 129 | 133 | 145 | 133 | 139 | 139 | 130 | 131 | 143 | 135 | 145 | 128 | |
switch | 117 | 118 | 118 | 117 | 115 | 120 | 114 | 113 | 116 | 118 | 117 | 120 | 113 | ||
1千万 | if | 81 | 68 | 82 | 75 | 76 | 68 | 69 | 79 | 91 | 75 | 76 | 91 | 68 | |
switch | 60 | 57 | 60 | 60 | 59 | 65 | 59 | 62 | 61 | 60 | 60 | 65 | 57 |
六、测试结果分析
纯数据的测试结果,很难进行分析,经过整理以后如下图:
if-100为if执行100条件式测试数,switch-100为switch执行100条件式测试数;
根据此图表结果,大家已经可能很清晰的看出 If与 Swtich的性能对比结果了。但是如此细微的性能差异,实现了业务就行了,何必关注这种费心又麻烦的事呢?
哈哈哈,性能测试更多时候,也是沟通问题,更是行政问题。
七、总结
这次验证过程,其实就是一次简单的性能测试过程,也就是——需求挖掘->明确目的->设计策略->准备环境->脚本编写->收集数据->结果分析->测试报告。此处略去的报告内容,因为不需要什么报告了。哈哈哈!(大家懂的)
就大量条件式的业务场景而言,除了利用switch以外,其实还可以用到枚举(enum)作为条件式,抽象每个判断式导向为函数式(function)。可能哪天我心情好会把利用enum优化的代码给放出来。
2015-8-23 15:17:18 跟新
策略模式+接口注入,写的是伪代码没有严格的语法规范,大家凑合着看。
interface Service{
public void execute();
public <T> T eval();
}
public class Strategy{
private static Concrunthashmap<String,function> content = new Concrunthashmap<String,function>();
public void register(String name,Clas<T> xxx){
if(!content.has(name)){
content.put(xxx);//这里隐去了反射生成对象的过程
}
}
public void execute(String name){
content.get(name).execute();
}
public void eval(String name){
content.get(name).eval();
}
}
class HelloServiceImpl implements Service{
public void execute(){
print "hello world";
}
public String eval(){
return "hello world";
}
}
class HiServiceImpl implements Service{
public void execute(){
print "hi world";
}
public String eval(){
return "hi world";
}
}