那个业务大拿死在了这个地方

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"业务代码中的技术是每个程序员的基础,但只是掌握了这些技巧,并不能成为技术大牛,还要不断打怪升级。Do more,Do better,Do exercise ,送给身边所有程序员 !!!"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/20/204f97b53061c8158f00f580d3ccbea5.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ca/caf57f343267d94fb277df57e806ade8.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/96/96746cf7bced09e1b53b3b28a947acb5.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一个工业级哈希表的要求:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"支持快速的查询、插入、删除操作"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"内存占用合理,不能浪费过多的内存空间"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"性能稳定,极端情况下,散列表的性能也不会退化到无法接受的情况"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d9/d9f9870d55a0b416d58fff810d138c62.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f5/f5becbfa4d9885920884e79f7777b284.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Java 8 中哈希表底层采用数组存储,利用 hash 算法计算出下标值来存储元素,再配合上动态扩容,才能成为大拿写业务代码的利器。在哈希表中,最最重要的是哈希函数,其次是如何解决哈希冲突。我们分别来看:"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"哈希算法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 Java 8 的源码中,hash函数的实现极其简单:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"static final int hash(Object key) {\n int h;\n return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"往数组中存储时,利用哈希值与数组长度做按位与运算,得到数组下标:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"if ((p = tab[i = (n - 1) & hash]) == null)\n\ttab[i] = newNode(hash, key, value, null);"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"参数key的 hashcode 是个整型值,内存中占了32个字节,右移16位的结果是前16位都变成了0。再与hashcode值做异或操作,新的hash值的前16位也都变成了0。新的hash值,在与数组长度做按位与运算,得到数组下标。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"举个例子,计算 \"helloworld\" 作为 key 存储时,数据下标的计算过程:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":" int h = \"hello\".hashCode();\n System.out.println(\"原始的hashcode值 :\" + getReplace(h));\n int t = h >>> 16;\n System.out.println(\"左移位16之后的值 :\" + getReplace(t));\n int r = h ^ t;\n System.out.println(\"异或结果 :\" + getReplace(r));\n int n = 15;\n System.out.println(\"数长度-1的哈希值 :\" + getReplace(n));\n int i = r & n;\n System.out.println(\"最终结果 :\" + getReplace(i));\n\n System.out.println(\"最终结果10进制 = \" + i);\n System.out.println(\"00000101111010010001100011010010\");\n}\n\nprivate static String getReplace(int r) {\n return String.format(\"%32s\", Integer.toBinaryString(r)).replace(' ', '0');\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"把计算过程的二进制运算,绘制在下图中:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/33/33f1897492499d0aa21f6d0490f4ce84.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最终结果 1011 转换为 10 进制为11,也就是以 “hello” 为 key 的元素,保存在数据下标 11 的位置。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"数组大小"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 hash(Object key) 函数中把 hash 值右移16位,刚是 32位字节的一半。再与自身异或,相当于用原始 hash 值的前半部分和后半部分混合,增加了 hash 的随机性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"与数组长度减一做按位与运算,相当于只保留了哈希值的低位值(后半部分)用来做数组下标。因此,要保证数组长度加一的 hash 值,高位为 0 低位都为 1。所以 HashMap 数组长度必须是 2 的整次幂,才能保证这一点。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/86/86c1e33686d722e00ffc6665850373f7.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"构造函数中的确有指定参数的方法,具体跟踪代码在真正执行赋值时,会执行如下函数:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"static final int tableSizeFor(int cap) {\n int n = cap - 1;\n n |= n >>> 1;\n n |= n >>> 2;\n n |= n >>> 4;\n n |= n >>> 8;\n n |= n >>> 16;\n return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先右移去掉低位数,再做按位或操作,相当于把结果固定在这样的范围:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2^{0},2^{1},2^{2},2^{3},2^{4},2^{5}……2^{n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因此即使是你传入了初始数组大小,也会调整最接近的长度范围,所以一定是2的整次幂"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"哈希冲突"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"再好的哈希算法也解决不了哈希冲突的问题,只能尽量的减少发生概率。那么如何处理真实发生的哈希冲突呢?"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1f/1f67997205d69205d9239e326d51f8bf.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Java 8 中除了用单链表解决哈希冲突外,还引入了红黑树。我们看一下源码 (java.util.HashMap#putVal):"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"for (int binCount = 0; ; ++binCount) {\n if ((e = p.next) == null) {\n p.next = newNode(hash, key, value, null);\n if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st\n treeifyBin(tab, hash);\n break;\n }\n if (e.hash == hash &&\n ((k = e.key) == key || (key != null && key.equals(k))))\n break;\n p = e;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当链表上的长度大于 TREEIFY_THRESHOLD - 1 时,调用 treeifyBin() 方法。TREEIFY_THRESHOLD 为 8,意味着,当链表上的数据大于等于7个时,链表升级为红黑树。具体红黑树的实现,请自己赏悦代码。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1b/1bbe8b628f452e52a526f00b278cac8d.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当数据大小需要从新计算时,在java.util.HashMap#resize 中调用 java.util.HashMap.TreeNode#split"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"if (loHead != null) {\n if (lc <= UNTREEIFY_THRESHOLD)\n tab[index] = loHead.untreeify(map);\n else {\n tab[index] = loHead;\n if (hiHead != null) // (else is already treeified)\n loHead.treeify(tab);\n }\n}\nif (hiHead != null) {\n if (hc <= UNTREEIFY_THRESHOLD)\n tab[index + bit] = hiHead.untreeify(map);\n else {\n tab[index + bit] = hiHead;\n if (loHead != null)\n hiHead.treeify(tab);\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果小于等于 UNTREEIFY_THRESHOLD (默认是6)执行 java.util.HashMap.TreeNode#untreeify,红黑树退化为链表。至于红黑树相关的代码,你还是自己查阅代码吧。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/08/0878b61055346855a80c677d1554cea7.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"写业务代码的程序员"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"每个技术人员都有个成为技术大牛的梦。工作后都会发现,梦想是成为大牛,但做的事情看起来跟大牛都不沾边。也总能听到有人说,“天天写业务代码还加班,如何才能成为技术大牛”。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"业务代码都写不好的程序员肯定无法成为技术大牛,只把业务代码写好的程序员也还不能成为技术大牛。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"写业务代码,一样可以有各种技巧,可以使得业务代码更具可扩展性,可以和产品经理多交流以便更好的理解和实现业务,可以做好日志记录提升故障定位效率……"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大拿是一个业务写的快的程序员,可能不是业务写的好的程序员。大拿也是一个想成为大牛的程序员,可能大拿只是想想什么也没做"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"业务代码中的技术是每个程序员的基础,但只是掌握了这些技巧,并不能成为技术大牛,还要不断打怪升级。送给所有奋斗在业务泥潭中的程序员三个锦囊:"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"Do more"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"熟悉更多的业务"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"了解系统的全貌"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"自学用到的框架"}]}]}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"Do better"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"改进不合理、可改进的地方"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"没发现有可以改进的地方,那说明功力不够,那就继续去发现"}]}]}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"Do exercise"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"功利学习"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"刻意练习"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"教会别人"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"关注我"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果您在微信阅读,请您点击链接 "},{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=MzI3MDYwOTYwOA==&scene=110#wechat_redirect","title":null},"content":[{"type":"text","text":"关注我"}]},{"type":"text","text":" ,如果您在 PC 上阅读请扫码关注我,欢迎与我交流随时指出错误"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/28/28d49266bc303e8d1d8779b0336982db.png","alt":null,"title":"小眼睛聊技术","style":[{"key":"width","value":"25%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章