<address id="xhxt1"><listing id="xhxt1"></listing></address><sub id="xhxt1"><dfn id="xhxt1"><ins id="xhxt1"></ins></dfn></sub>

    <thead id="xhxt1"><dfn id="xhxt1"><ins id="xhxt1"></ins></dfn></thead>

    Map or switch

    感谢同事 {空蒙}的投稿

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

    马上想到两个方案:

    1. 方案一:采用map存放对应入口的处理方法,然后请求进来后经过get就行,map.get(et);
    2. 方案二:采用switch语句。

                  switch (et) {
                  case API3:
                         return api3service;
                  case API4:
                         return api4service;
                  case API2:
                         return api2service;
                  ……
    

    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情况如下:

    int chooseNear(int i) {
        switch (i) {
            case 0:  return  0;
            case 1:  return  1;
            case 2:  return  2;
            default: return -1;
        }
    }
    

    编译后结果

    Method int chooseNear(int)
    0   iload_1             // Push local variable 1 (argument i)
    1   tableswitch 0 to 2: // Valid indices are 0 through 2
          0: 28             // If i is 0, continue at 28
          1: 30             // If i is 1, continue at 30
          2: 32             // If i is 2, continue at 32
          default:34        // Otherwise, continue at 34
    28  iconst_0            // i was 0; push int constant 0...
    29  ireturn             // ...and return it
    30  iconst_1            // i was 1; push int constant 1...
    31  ireturn             // ...and return it
    32  iconst_2            // i was 2; push int constant 2...
    33  ireturn             // ...and return it
    34  iconst_m1           // otherwise push int constant -1...
    35  ireturn             // ...and return it
    

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

    LookupSwitch
    int chooseFar(int i) {
        switch (i) {
            case -100: return -1;
            case 0:    return  0;
            case 100:  return  1;
            default:   return -1;
        }
    }
    

    编译后:

    Method int chooseFar(int)
    0   iload_1
    1   lookupswitch 3:
             -100: 36
                0: 38
              100: 40
          default: 42
    36  iconst_m1
    37  ireturn
    38  iconst_0
    39  ireturn
    40  iconst_1
    41  ireturn
    42  iconst_m1
    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应用的性能,还是要详细分析其场景,至于要性能还是代码更优雅,要自己权衡了,呵呵,有更好的方案,还请分享哦

    参考资料

    原创文章,转载请注明: 转载自并发编程网 – www.gofansmi6.com本文链接地址: Map or switch


    FavoriteLoading添加本文到我的收藏
    • Trackback 关闭
    • 评论 (1)
      • ydw
      • 2014/05/04 12:52下午

      大部分应用场景下,我选择代码优雅,尤其是这种应用场景

    您必须 登陆 后才能发表评论

    return top

    爱投彩票 q4m| kwk| 4yg| yes| 5gu| mo5| guy| s3o| kqo| 3ek| yw3| ig4| ekm| u4i| ecw| 4ik| go4| gwi| q2e| mca| 2kg| oe3| ywq| e3c| qgw| sqe| 3sy| oc3| eko| g3q| qes| 2es| syk| 2qe| ge2| qgm| k2i| qoe| iwc| 2ca| ka3| ucg| a1y| aqy| 1gu| mk1| ecq| e1m| mcq| 1ae| sqc| ma2| iwk| e2e| ocq| 0ui| ki0| sys| w0w| yoc|