电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

你所不知道的Java之HashCode

瀏覽:79日期:2022-09-05 13:19:54

之所以寫HashCode,是因為平時我們總聽到它。但你真的了解hashcode嗎?它會在哪里使用?它應該怎樣寫?

相信閱讀完本文,能讓你看到不一樣的hashcode。

使用hashcode的目的在于:使用一個對象查找另一個對象。對于使用散列的數據結構,如 HashSet、HashMap、LinkedHashSet、LinkedHashMap ,如果沒有很好的覆寫鍵的hashcode()和equals()方法,那么將無法正確的處理鍵。

請對以下代碼中 Person 覆寫hashcode()方法,看看會發生什么?

// 覆寫hashcode@Overridepublic int hashCode() { return age;}@Testpublic void testHashCode() { Set<Person> people = new HashSet<Person>(); Person person = null; for (int i = 0; i < 3 ; i++) {person = new Person('name-' + i, i);people.add(person); } person.age = 100; System.out.println(people.contains(person)); people.add(person); System.out.println(people.size());}

運行結果并不是預期的 true 和 3 ,而是 false 和 4 !改變 person.age 后HashSet無法找到 person 這個對象了,可見覆寫hahcode對HashSet的存儲和查詢造成了影響。

那么hashcode是如何影響HashSet的存儲和查詢呢?又會造成怎樣的影響呢?

HashSet的內部使用HashMap實現,所有放入HashSet中的集合元素都會轉為HashMap的key來保存。HashMap使用散列表來存儲,也就是數組+鏈表+紅黑樹(JDK1.8增加了紅黑樹部分)。

存儲結構簡圖如下:

你所不知道的Java之HashCode

HashMap存儲結構簡圖

數組的默認長度為16,數組里每個元素存儲的是一個鏈表的頭結點。組成鏈表的結點結構如下:

static class Node<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Node<K,V> next; ...}

每一個Node都保存了一個hash----鍵對象的hashcode,如果鍵沒有按照任何特定順序保存,查找時通過equals()逐一與每一個數組元素進行比較,那么時間復雜度為O(n),數組長度越大,效率越低。

所以瓶頸在于鍵的查詢速度,如何通過鍵來快速的定位到存儲位置呢?

HashMap將鍵的hash值與數組下標建立映射,通過鍵對象的hash函數生成一個值,以此作為數組的下標,這樣我們就可以通過鍵來快速的定位到存儲位置了。如果hash函數設計的完美的話,數組的每個位置只有較少的值,那么在O(1)的時間我們就可以找到需要的元素,從而不需要去遍歷鏈表。這樣就大大提高了查詢速度。

那么HashMap根據hashcode是如何得到數組下標呢?可以拆分為以下幾步:

第一步: h = key.hashCode() 第二步: h ^ (h >>> 16) 第三步: (length - 1) & hash

分析

第一步是得到key的hashcode值;

第二步是將鍵的hashcode的高16位異或低16位(高位運算),這樣即使數組table的length比較小的時候,也能保證高低Bit都參與到Hash的計算中,同時不會有太大的開銷;

第三步是hash值和數組長度進行取模運算,這樣元素的分布相對來說比較均勻。當length總是2的n次方時, h & (length-1) 運算等價于對length取模,這樣模運算轉化為位移運算速度更快。

但是,HashMap默認數組初始化容量大小為16。當數組長度遠小于鍵的數量時,不同的鍵可能會產生相同的數組下標,也就是發生了哈希沖突!

對于哈希沖突有開放定址法、鏈地址法、公共溢出區法等解決方案。

開放定址法就是一旦發生沖突,就尋找下一個空的散列地址。過程可用下式描述:

f i (key) = (f(key) + d i ) mod m (d i =1,2,3,...,m-1)

例如鍵集合為 {12,67,56,16,25,37,22,29,15,47,48,34} ,表長 n = 12 ,取 f(key) = key mod 12 。

前5個計算都沒有沖突,直接存入。如表所示

數組下標 鍵0 121 252 3 4 165 6 7 678 569 10 11

當 key = 37 時, f(37) = 1 ,與25的位置沖突。應用公式 f(37) = (f(37) + 1) mod 12 = 2 ,所以37存入數組下標為2的位置。如表所示

數組下標 鍵0 121 252 373 4 165 6 7 678 569 10 11

到了 key = 48 ,與12所在的0沖突了。繼續往下找,發現一直到 f(48) = (f(48) + 6) mod 12 = 6 時才有空位。如表所示

數組下標 鍵0 121 252 373 4 165 296 487 678 569 10 2211 47

所以在解決沖突的時候還會出現48和37沖突的情況,也就是出現了 堆積 ,無論是查找還是存入效率大大降低。

鏈地址法解決沖突的做法是:如果哈希表空間為 [0~m-1] ,設置一個由m個指針分量組成的一維數組 Array[m] , 凡哈希地址為i的數據元素都插入到頭指針為 Array[i] 的鏈表中。

它的基本思想是:為每個Hash值建立一個單鏈表,當發生沖突時,將記錄插入到鏈表中。如圖所示:

你所不知道的Java之HashCode

鏈地址法

鏈表的好處表現在:

remove操作時效率高,只維護指針的變化即可,無需進行移位操作 重新散列時,原來散落在同一個槽中的元素可能會被散落在不同的地方,對于數組需要進行移位操作,而鏈表只需維護指針。 但是,這也帶來了需要遍歷單鏈表的性能損耗。

公共溢出法就是我們為所有沖突的鍵單獨放一個公共的溢出區存放。

例如前面例子中 {37,48,34} 有沖突,將他們存入溢出表。如圖所示。

你所不知道的Java之HashCode

公共溢出法

在查找時,先與基本表進行比對,如果相等則查找成功,如果不等則在溢出表中進行順序查找。公共溢出法適用于沖突數據很少的情況。

HashMap解決沖突采取的是鏈地址法。整體流程圖(暫不考慮擴容)如下:

你所不知道的Java之HashCode

HashMap存儲流程簡圖

理解了hashcode和哈希沖突即解決方案后,我們如何設計自己的hashcode()

方法呢?

Effective Java一書中對覆寫hashcode()給出以下指導:

給int變量result賦予某個非零常量值

為對象內每個有意義的域f計算一個int散列碼c

域類型 計算boolean c = (f ? 0 : 1)byte、char、short、int c = (int)flong c = (int)(f ^ (f >>> 32))float c = Float.floatToIntBits(f)double long l = Double.doubleToIntLongBits(f) c = (int)(l ^ (l >>> 32))Object c = f.hashcode()數組 每個元素應用上述規則boolean c = (f ? 0 : 1)boolean c = (f ? 0 : 1) 合并計算得到散列碼 result = 37 * result + c

現代IDE通過點擊右鍵上下文菜單可以自動生成hashcode方法,比如通過IDEA生成的hashcode如下:

@Overridepublic int hashCode() { int result = name != null ? name.hashCode() : 0; result = 31 * result + age; return result;}

但是在企業級代碼中,最好使用第三方庫如 Apache commons 來生成hashocde方法。使用第三方庫的優勢是可以反復驗證嘗試代碼。下面代碼顯示了如何使用 Apache Commons hash code 為一個自定義類構建生成hashcode。

public int hashCode(){ HashCodeBuilder builder = new HashCodeBuilder(); builder.append(mostSignificantMemberVariable); ........................ builder.append(leastSignificantMemberVariable); return builder.toHashCode();}

如代碼所示,最重要的簽名成員變量應該首先傳遞然后跟隨的是沒那么重要的成員變量。

總結

通過上述分析,我們設計hashcode()應該注意的是:

無論何時,對同一個對象調用hashcode()都應該生成同樣的值。 hashcode()盡量使用對象內有意義的識別信息。 好的hashcode()應該產生分布均勻的散列值。

來自: http://www.jianshu.com/p/e183f75d0289

標簽: Java
相關文章:
主站蜘蛛池模板: 亿立分板机_曲线_锯片式_走刀_在线式全自动_铣刀_在线V槽分板机-杭州亿协智能装备有限公司 | LED显示屏_LED屏方案设计精准报价专业安装丨四川诺显科技 | 外贸网站建设-外贸网站设计制作开发公司-外贸独立站建设【企术】 | 江苏皓越真空设备有限公司 | ASA膜,ASA共挤料,篷布色母料-青岛未来化学有限公司 | 大鼠骨髓内皮祖细胞-小鼠神经元-无锡欣润生物科技有限公司 | 空调风机,低噪声离心式通风机,不锈钢防爆风机,前倾皮带传动风机,后倾空调风机-山东捷风风机有限公司 | 胃口福饺子加盟官网_新鲜现包饺子云吞加盟 - 【胃口福唯一官网】 | 丹佛斯压力传感器,WISE温度传感器,WISE压力开关,丹佛斯温度开关-上海力笙工业设备有限公司 | 菏泽知彼网络科技有限公司| 塑料脸盆批发,塑料盆生产厂家,临沂塑料广告盆,临沂家用塑料盆-临沂市永顺塑业 | 天津次氯酸钠酸钙溶液-天津氢氧化钠厂家-天津市辅仁化工有限公司 | 蜘蛛车-高空作业平台-升降机-高空作业车租赁-臂式伸缩臂叉装车-登高车出租厂家 - 普雷斯特机械设备(北京)有限公司 | 壹作文_中小学生优秀满分作文大全 | 微信小程序定制,广州app公众号商城网站开发公司-广东锋火 | 全自动端子机|刺破式端子压接机|全自动双头沾锡机|全自动插胶壳端子机-东莞市傅氏兄弟机械设备有限公司 | 广州物流公司_广州货运公司_广州回程车运输 - 万信物流 | CXB船用变压器-JCZ系列制动器-HH101船用铜质开关-上海永上船舶电器厂 | 超细粉碎机|超微气流磨|气流分级机|粉体改性设备|超微粉碎设备-山东埃尔派粉碎机厂家 | 今日扫码_溯源二维码_产品防伪一物一码_红包墙营销方案 | 卓能JOINTLEAN端子连接器厂家-专业提供PCB接线端子|轨道式端子|重载连接器|欧式连接器等电气连接产品和服务 | 浙江筋膜枪-按摩仪厂家-制造商-肩颈按摩仪哪家好-温州市合喜电子科技有限公司 | 精密五金冲压件_深圳五金冲压厂_钣金加工厂_五金模具加工-诚瑞丰科技股份有限公司 | 贝壳粉涂料-内墙腻子-外墙腻子-山东巨野七彩贝壳漆业中心 | 节流截止放空阀-不锈钢阀门-气动|电动截止阀-鸿华阀门有限公司 | 锂电混合机-新能源混合机-正极材料混料机-高镍,三元材料混料机-负极,包覆混合机-贝尔专业混合混料搅拌机械系统设备厂家 | 除尘器布袋骨架,除尘器滤袋,除尘器骨架,电磁脉冲阀膜片,卸灰阀,螺旋输送机-泊头市天润环保机械设备有限公司 | 螺杆泵_中成泵业 | 东莞画册设计_logo/vi设计_品牌包装设计 - 华略品牌设计公司 | 北京浩云律师事务所-企业法律顾问_破产清算等公司法律服务 | 天津仓库出租网-天津电商仓库-天津云仓一件代发-【博程云仓】 | 塑胶地板-商用PVC地板-pvc地板革-安耐宝pvc塑胶地板厂家 | 冷却塔改造厂家_不锈钢冷却塔_玻璃钢冷却塔改造维修-广东特菱节能空调设备有限公司 | J.S.Bach 圣巴赫_高端背景音乐系统_官网| 工业风机_环保空调_冷风机_工厂车间厂房通风降温设备旺成服务平台 | 四探针电阻率测试仪-振实密度仪-粉末流动性测定仪-宁波瑞柯微智能 | 沈阳激光机-沈阳喷码机-沈阳光纤激光打标机-沈阳co2激光打标机 | 标准品网_标准品信息网_【中检计量】 | 浴室柜-浴室镜厂家-YINAISI · 意大利设计师品牌 | 咿耐斯 |-浙江台州市丰源卫浴有限公司 | 儿童乐园|游乐场|淘气堡招商加盟|室内儿童游乐园配套设备|生产厂家|开心哈乐儿童乐园 | 网带通过式抛丸机,,网带式打砂机,吊钩式,抛丸机,中山抛丸机生产厂家,江门抛丸机,佛山吊钩式,东莞抛丸机,中山市泰达自动化设备有限公司 |