业务代码中的技术是每个程序员的基础,但只是掌握了这些技巧,并不能成为技术大牛,还要不断打怪升级。Do more,Do better,Do exercise ,送给身边所有程序员 !!!

一个工业级哈希表的要求:

  • 支持快速的查询、插入、删除操作
  • 内存占用合理,不能浪费过多的内存空间
  • 性能稳定,极端情况下,散列表的性能也不会退化到无法接受的情况


    Java 8 中哈希表底层采用数组存储,利用 hash 算法计算出下标值来存储元素,再配合上动态扩容,才能成为大拿写业务代码的利器。在哈希表中,最最重要的是哈希函数,其次是如何解决哈希冲突。我们分别来看:

哈希算法

在 Java 8 的源码中,hash函数的实现极其简单:

static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

往数组中存储时,利用哈希值与数组长度做按位与运算,得到数组下标:

if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);

参数key的 hashcode 是个整型值,内存中占了32个字节,右移16位的结果是前16位都变成了0。再与hashcode值做异或操作,得到新的hash值。再用新的hash值,在与数组长度做按位与运算,得到数组下标。

举个例子,计算 “helloworld” 作为 key 存储时,数据下标的计算过程:

    int h = "hello".hashCode();System.out.println("原始的hashcode值     :" + getReplace(h));int t = h >>> 16;System.out.println("左移位16之后的值      :" + getReplace(t));int r = h ^ t;System.out.println("异或结果             :" + getReplace(r));int n = 15;System.out.println("数长度-1的哈希值      :" + getReplace(n));int i = r & n;System.out.println("最终结果             :" + getReplace(i));System.out.println("最终结果10进制 = " + i);System.out.println("00000101111010010001100011010010");
}private static String getReplace(int r) {return String.format("%32s", Integer.toBinaryString(r)).replace(' ', '0');
}

把计算过程的二进制运算,绘制在下图中:

最终结果 1011 转换为 10 进制为11,也就是以 “hello” 为 key 的元素,保存在数据下标 11 的位置。

数组大小

在 hash(Object key) 函数中把 hash 值右移16位,刚是 32位字节的一半。再与自身异或,相当于用原始 hash 值的前半部分和后半部分混合,增加了 hash 的随机性。

与数组长度减一做按位与运算,相当于只保留了哈希值的低位值(后半部分)用来做数组下标。因此,要保证数组长度加一的 hash 值,高位为 0 低位都为 1。所以 HashMap 数组长度必须是 2 的整次幂,才能保证这一点。

构造函数中的确有指定参数的方法,具体跟踪代码在真正执行赋值时,会执行如下函数:

static final int tableSizeFor(int cap) {int n = cap - 1;n |= n >>> 1;n |= n >>> 2;n |= n >>> 4;n |= n >>> 8;n |= n >>> 16;return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}

先右移去掉低位数,再做按位或操作,相当于把结果固定在这样的范围:
20,21,22,23,24,25……2n2^{0},2^{1},2^{2},2^{3},2^{4},2^{5}……2^{n} 20,21,22,23,24,25……2n
因此即使是你传入了初始数组大小,也会调整最接近的长度范围,所以一定是2的整次幂

哈希冲突

再好的哈希算法也解决不了哈希冲突的问题,只能尽量的减少发生概率。那么如何处理真实发生的哈希冲突呢?

Java 8 中除了用单链表解决哈希冲突外,还引入了红黑树。我们看一下源码 (java.util.HashMap#putVal):

for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {p.next = newNode(hash, key, value, null);if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);break;}if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;
}

当链表上的长度大于 TREEIFY_THRESHOLD - 1 时,调用 treeifyBin() 方法。TREEIFY_THRESHOLD 为 8,意味着,当链表上的数据大于等于7个时,链表升级为红黑树。具体红黑树的实现,请自己赏悦代码。

当数据大小需要从新计算时,在java.util.HashMap#resize 中调用 java.util.HashMap.TreeNode#split

if (loHead != null) {if (lc <= UNTREEIFY_THRESHOLD)tab[index] = loHead.untreeify(map);else {tab[index] = loHead;if (hiHead != null) // (else is already treeified)loHead.treeify(tab);}
}
if (hiHead != null) {if (hc <= UNTREEIFY_THRESHOLD)tab[index + bit] = hiHead.untreeify(map);else {tab[index + bit] = hiHead;if (loHead != null)hiHead.treeify(tab);}
}

如果小于等于 UNTREEIFY_THRESHOLD (默认是6)执行 java.util.HashMap.TreeNode#untreeify,红黑树退化为链表。至于红黑树相关的代码,你还是自己查阅代码吧。

写业务代码的程序员

每个技术人员都有个成为技术大牛的梦。工作后都会发现,梦想是成为大牛,但做的事情看起来跟大牛都不沾边。也总能听到有人说,“天天写业务代码还加班,如何才能成为技术大牛”。

业务代码都写不好的程序员肯定无法成为技术大牛,只把业务代码写好的程序员也还不能成为技术大牛。

写业务代码,一样可以有各种技巧,可以使得业务代码更具可扩展性,可以和产品经理多交流以便更好的理解和实现业务,可以做好日志记录提升故障定位效率……

大拿是一个业务写的快的程序员,可能不是业务写的好的程序员。大拿也是一个想成为大牛的程序员,可能大拿只是想想什么也没做

业务代码中的技术是每个程序员的基础,但只是掌握了这些技巧,并不能成为技术大牛,还要不断打怪升级。送给所有奋斗在业务泥潭中的程序员三个锦囊:

Do more

  • 熟悉更多的业务
  • 了解系统的全貌
  • 自学用到的框架

Do better

  • 改进不合理、可改进的地方
  • 没发现有可以改进的地方,那说明功力不够,那就继续去发现

Do exercise

  • 功利学习
  • 刻意练习
  • 教会别人

关注我

如果您在微信阅读,请您点击链接 关注我 ,如果您在 PC 上阅读请扫码关注【小眼睛聊技术】,欢迎与我交流随时指出错误

那个业务大拿死在了这个地方相关推荐

  1. 失落的帝国:盛大业务大收缩

    "现在盛大的员工见面打招呼,就是彼此询问'什么时候离职'."5月23日,一位盛大的员工告诉记者,自盛大网络(NSDAQ:SNDA)(以下简称盛大)宣布私有化之后,公司规模性裁员与离 ...

  2. 农分期 java_农分期现行业务大揭密,8项业务为农户提供全方向服务

    原标题:农分期现行业务大揭密,8项业务为农户提供全方向服务 "农分期,怎么办?"兄弟,农分期业务太多了,你问我怎么办,我该怎么回答你呢? 所以今天我得帮大家把农分期能替你解决的问题 ...

  3. 中国历史上十大冤死名将

    中国历史上十大冤死名将 冤 死将,顾名思义,要大大的名将,大大的冤枉,小小的不算,最典型的比如岳王,袁大督师,标准有几个:蒙冤指数,很好理解.惨烈指数,指死状之惨.影响指 数,对战局,时局的影响,以蒙 ...

  4. 计算机大比武大练兵实施方案,2016年“岗位大练兵、业务大比武”活动实施方案...

    税务总局日前印发<2016年"岗位大练兵.业务大比武"活动实施方案>(以下简称<方案>),明确了活动的总体要求.主要任务.结果运用.组织实施等内容,标志着为 ...

  5. 【转帖】十大已死和垂死IT技术和职业

    国际上颇具声望的 ComputerWorld (国内< 计算机世界>的美国原版)两天前由一位女记者撰写的 "The top 10 dead (or dying) computer ...

  6. 直播技术:从性能参数到业务大数据,浅谈直播CDN服务监控

    线上服务的有效监控和数据收集,一直是后端服务离不开的话题.直播作为一种经典的分布式系统,监控以及数据收集更是必不可少的工作.如何对海量的服务集群有效的监控和保活,又如何抓取集群中的碎片数据中来优化服务 ...

  7. 从性能参数到业务大数据,浅谈直播CDN服务监控

    线上服务的有效监控和数据收集,一直是后端服务离不开的话题.直播cdn作为一种经典的分布式系统,监控以及数据收集更是必不可少的工作.如何对海量的服务集群有效的监控和保活,又如何抓取集群中的碎片数据中来优 ...

  8. 推荐一款开源跨平台 [业务大屏,数据报表] 快速开发平台

    Jeecg-Boot是一款基于SpringBoot+代码生成器的快速开发平台!采用前后端分离架构:SpringBoot,Mybatis,Shiro,JWT,Vue&Ant Design.Jee ...

  9. 小米Q3财报解读:手机行业整体疲软之下,怎么做到互联网业务大爆发?

    11月23日,小米集团发布了2021年第三季度财报.财报显示,第三季度总营收为781亿元,同比增长8.2%:经调整净利润52亿元,同比增长25.4%. 具体业务上看,2021年Q3小米智能手机收入47 ...

最新文章

  1. Spring-boot+Vue = Fame 写blog的一次小结
  2. php能力模型,ThinkPHP5--基础篇(模型)
  3. 优化器 Adam和SGD的结合体AdaBelief
  4. php: xampp安装对应的phalcon版本(3.2.2-php5.6):比如redis-php5.6, php_igbinary-5.6
  5. 蛋白质折叠的霰弹枪方法
  6. Delphi编程技术简介
  7. mysql登录之后可以写什么_MYSQL登陆完之后如何操作???(新手求助)
  8. 简单的根据parentId生成树
  9. 计算机硬件基础与linux发展史
  10. 【BZOJ3524】Couriers,第一次的主席树
  11. 如何用 R 语言的 Shiny 库编写 web 程序
  12. java实验报告的原理_JAVA实验报告
  13. linux 传真 邮件,基于Linux的传真系统(转)
  14. 百度地图开放平台web api 获取上海市所有小区信息
  15. Resolver error Error Downloading VS Code Server failed - please install either curl or wget on the
  16. Excel之match index 和vlookup函数 和双条件查找匹配
  17. 零售商店订单数据分析
  18. 北京个人社保查询方法
  19. nginx cache 总结
  20. 傍上阿里系大款,禧云国际如何守住“自由身”?

热门文章

  1. Java-对象数组以及内存图解
  2. LINUX学习网址精选
  3. 来自 http://www.seeitco.com/ 的各大IT公司薪资和待遇内幕(不断更新)[找工作的朋友必读!]
  4. BootstrapDialog.show函数底层简化
  5. 中国首个开源基金会成立;京东 AI 研究院获 QuAC 机器阅读理解竞赛冠军
  6. 教你如何搜索pois(兴趣点),制作可视化作品
  7. 百度涉嫌干涉以色列广播虚假新闻被黑
  8. chrome浏览器的下载地址
  9. 素民党的故事 (01) 什么是素民党
  10. Java中七大垃圾回收器