Map or switch

感谢同事 {空蒙}的投稿

最近碰到个场景,还蛮有普遍性的,如mtop的请求入口非常多,有api2,api3,api4,h5,h5update,cdn_cache,bigpipe等,而Mtop需要依据其具体的入口,选择不同的业务逻辑进行对应的处理。

马上想到两个方案:

  1. 方案一:采用map存放对应入口的处理方法,然后请求进来后经过get就行,map.get(et);
  2. 方案二:采用switch语句。
1 switch (et) {
2 case API3:
3        return api3service;
4 case API4:
5        return api4service;
6 case API2:
7        return api2service;
8 ……

if else这种就不予考虑了,明显采用map显的更优雅,代码更具可维护性,目前mtop存在6个入口,api4还未上,如果用switch每次需要硬编码那性能呢?

但用map,也可以做些优化处理,比如我发现api3、h5、cdn_cache在map默认大小为16下,其桶位置发生了碰撞,这样每次get的时候就需要遍历了,这是不好,当然有两种方案,一是改key值避免碰撞,二是改map大小,让其不发生碰撞,我采用map大小为64,避免碰撞,当然后面如要继续添加时候,需要关注经测试,性能可以提升44%,(本机场景,并且这个key在桶的最尾部,也就是需要全部遍历桶全部数据的场景,并且全部预先执行1w次,摒弃了jit对结果的影响)

但map的get操作,每次需要进行hash,位移操作,&操作,再比较操作,想想就需要很多的指令要执行完才能拿到

如果是switch呢?Switch在编译后,有LookupSwitch 和 TableSwitch,其中TableSwitch是O(1)的,LookupSwitch是 O(log n),TableSwitch情况如下:

1 int chooseNear(int i) {
2     switch (i) {
3         case 0return  0;
4         case 1return  1;
5         case 2return  2;
6         default: return -1;
7     }
8 }

编译后结果

01 Method int chooseNear(int)
02 0   iload_1             // Push local variable 1 (argument i)
03 1   tableswitch 0 to 2: // Valid indices are 0 through 2
04       0: 28             // If i is 0, continue at 28
05       1: 30             // If i is 1, continue at 30
06       2: 32             // If i is 2, continue at 32
07       default:34        // Otherwise, continue at 34
08 28  iconst_0            // i was 0; push int constant 0...
09 29  ireturn             // ...and return it
10 30  iconst_1            // i was 1; push int constant 1...
11 31  ireturn             // ...and return it
12 32  iconst_2            // i was 2; push int constant 2...
13 33  ireturn             // ...and return it
14 34  iconst_m1           // otherwise push int constant -1...
15 35  ireturn             // ...and return it

也就是TableSwitch只要计算一次偏移量,立即就能到case执行,其时间复杂度为O(1)

1 LookupSwitch
2 int chooseFar(int i) {
3     switch (i) {
4         case -100: return -1;
5         case 0:    return  0;
6         case 100return  1;
7         default:   return -1;
8     }
9 }

编译后:

01 Method int chooseFar(int)
02 0   iload_1
03 1   lookupswitch 3:
04          -100: 36
05             0: 38
06           100: 40
07       default: 42
08 36  iconst_m1
09 37  ireturn
10 38  iconst_0
11 39  ireturn
12 40  iconst_1
13 41  ireturn
14 42  iconst_m1
15 43  ireturn

也就是LookupSwitch编译后会保证其顺序,并采用二分法查找对应的case,其时间复杂度为O(log n)
本机,全部预先执行1w次跳过jit的影响,采用map与switch各执行1亿次,执行时间是两位数的差距,map为400多ms,switch为5ms

当然测试的场景case都比较少,如果达到1k多个case条件呢? Jit还会把jvm指令缓存吗?,如果不缓存又是另外的情况了
大家可以把eclipse设置Xint,看看屏蔽jit后大量运行的效果
还有switch在什么场景下编译后会是TableSwitch,什么下会是LookupSwitch,毕竟两者的时间复杂度还是有差距
Java应用的性能,还是要详细分析其场景,至于要性能还是代码更优雅,要自己权衡了,呵呵,有更好的方案,还请分享哦

时间: 2024-08-05 07:10:48

Map or switch的相关文章

在终端中优雅地编写Python

前言 最早我也只是在服务器上编辑文件的时候用用vim来改改程序,并没有把vim当做自己的主力编辑器.但是偶然的一次机会需要改一个奇葩的输入文件的格式,用了下Vim的宏录制,尝到了甜头,于是后面就开始用Vim来写程序了,虽然使用初期有些阻力,但时间久了就会发现,双手再也不用离开键盘,即使使用sublime这样的编辑器我也要改成使用Vim模式,Vim真的能让自己一思维的速度编辑文本(对我来说就是写程序了). 正好最近换了新的电脑,需要在新电脑上配置一下,就干脆把一些配置相关的东东记录下来吧,相关的配

android解析JSON数据_Android

JSONObject的使用  一. JSON对象的使用: String content = "{'username': 'linux', 'password': '123456'}"; JSONObject jsonObject = new JSONObject(content); String username = jsonObject.getString("username"); String password = jsonObject.getString(&q

php版新浪支付接口之回调接口笔记

所谓的新浪回调接口,就是当资金充值成功之后,新浪会通知本网站一个信息来告知已经充值成功,并返回一组成功的数组,我们根据这些数据再进行一个本地数据库的同步操作,比如更新本地数据库是否充值成功的状态,由未支付改为已支付,然后再网站上面进行显示,还有资金也会进行同步 update,好了,下面夏日博客把日志分享下来,接着上一篇新浪支付新建接口,一个简单的新浪支付回调接口,这个异步回调的信息是通过日志生成到ftp下面的,根据日志进行本地的更新. 一,先查看日志文件 说明:数据提交到新浪之后会生成新浪日志文

android解析JSON数据

JSONObject的使用 一. JSON对象的使用: String content = "{'username': 'linux', 'password': '123456'}"; JSONObject jsonObject = new JSONObject(content); String username = jsonObject.getString("username"); String password = jsonObject.getString(&qu

uva 704 - Colour Hash map+双向bfs

     这题从上周日开始想写的,结果拖了一周--现在写题效率越来越低了,要计时训练了.      一道很复杂的隐式图搜索--状态数有24位,看到网上有人按11进制转换,然后对Hash_MAX取模,个人认为这种做法不靠谱.于是把状态变成string类型的,直接用map,对于map,24位的string效率还是非常高的.     搜索的时候双向bfs,因为比较懒,所以把反向和正向写一起了,其实逆向只用做一次就可以了.     还有一点要注意的就是要输出数字最小的一种变换序号,这个处理方法就是把一层

css3实现的switch滑动开关按钮的效果

本身我做这一个ui的目的是支持移动端的页面,而webkit上也正好支持单标记的input元素是使用伪类(:before或:after),所以我没做更多的支持和优化,我只是想尽量的保持html干净,所以没用其他元素做模拟.如果你要使用在桌面应用上,或支持其他浏览器,可以自己稍微修改一下,反正我是没测试过.   今天继续分享一个iOS风格的switch开关按钮,样子也非常常见,如图:     主要是使用了<input  type="checkbox">来模拟实现,具体的HTML

(6)Xamarin.android google map v2

原文 Xamarin.android google map v2 Google Map v1已经在2013年的3月开始停止支持了,目前若要在你的Android手机上使用到Google Map,就必须要使用到Google Map v2的版本.在Xamarin要使用Google Map v2之前,必需要做的动作有下列几项: 从你计算机里的keystore里面查询个人的指纹凭证(SHA1). 用SHA1指纹凭证到Google APIs网站申请key. 在Xamarin里的Android SDK Man

android-Android使用Switch控件会出现一串数字

问题描述 Android使用Switch控件会出现一串数字 JAVA代码部分 private void initLeftMenu() { //侧滑菜单初始化函数,设置Adapter this.initTextSwitch("自动更新"); this.initTextSwitch("多彩界面"); this.initExpList(); this.initExpList(); this.leftMenuListDataAdapter1 = new SimpleAdap

有好的方法可以替代 if else 和switch吗?

问题描述 if(name.equals("name")||name.equals("name")||name.equals("name")||name.equals("name")||name.equals("name")||name.equals("name")||name.equals("name")||name.equals("name")|