Java中對HashMap的深度分析與比較(轉)
Java中對HashMap的深度分析與比較(轉)[@more@]在Java的世界裡,無論類還是各種資料,其結構的處理是整個程式的邏輯以及效能的關鍵。由於本人接觸了一個有關效能與邏輯同時並存的問題,於是就開始研究這方面的問題。找遍了大大小小的論壇,也把《Java 虛擬機器規範》,《apress,.java.collections.(2001),.bm.ocr.6.0.shareconnector》,和《Thinking in Java》翻了也找不到很好的答案,於是一氣之下把JDK的 src 解壓出來研究,擴然開朗,遂寫此文,跟大家分享感受和順便驗證我理解還有沒有漏洞。 這裡就拿HashMap來研究吧。
HashMap可謂JDK的一大實用工具,把各個Object對映起來,實現了“鍵--值”對應的快速存取。但實際裡面做了些什麼呢?
在這之前,先介紹一下負載因子和容量的屬性。大家都知道其實一個 HashMap 的實際容量就 因子*容量,其預設值是 16×0.75=12; 這個很重要,對效率很一定影響!當存入HashMap的物件超過這個容量時,HashMap 就會重新構造存取表。這就是一個大問題,我後面慢慢介紹,反正,如果你已經知道你大概要存放多少個物件,最好設為該實際容量的能接受的數字。
兩個關鍵的方法,put和get:
先有這樣一個概念,HashMap是宣告瞭 Map,Cloneable, Serializable 介面,和繼承了 AbstractMap 類,裡面的 Iterator 其實主要都是其內部類HashIterator 和其他幾個 iterator 類實現,當然還有一個很重要的繼承了Map.Entry 的 Entry 內部類,由於大家都有原始碼,大家有興趣可以看看這部分,我主要想說明的是 Entry 內部類。它包含了hash,value,key 和next 這四個屬性,很重要。put的原始碼如下
public Object put(Object key, Object value) {
Object k = maskNull(key);
這個就是判斷鍵值是否為空,並不很深奧,其實如果為空,它會返回一個static Object 作為鍵值,這就是為什麼HashMap允許空鍵值的原因。
int hash = hash(k);
int i = indexFor(hash, table.length);
這連續的兩步就是 HashMap 最牛的地方!研究完我都汗顏了,其中 hash 就是透過 key 這個Object的 hashcode 進行 hash,然後透過 indexFor 獲得在Object table的索引值。
table???不要驚訝,其實HashMap也神不到哪裡去,它就是用 table 來放的。最牛的就是用 hash 能正確的返回索引。其中的hash演算法,我跟JDK的作者 Doug 聯絡過,他建議我看看《The art of programing vol3》可恨的是,我之前就一直在找,我都找不到,他這樣一提,我就更加急了,可惜口袋空空啊!!!
不知道大家有沒有留意 put 其實是一個有返回的方法,它會把相同鍵值的 put 覆蓋掉並返回舊的值!如下方法徹底說明了 HashMap 的結構,其實就是一個表加上在相應位置的Entry的連結串列:
for (Entry e = table; e != null; e = e.next) {
if (e.hash == hash && eq(k, e.key)) {
Object oldvalue = e.value;
e.value = value; //把新的值賦予給對應鍵值。
e.recordAccess(this); //空方法,留待實現
return oldvalue; //返回相同鍵值的對應的舊的值。
}
}
modCount++; //結構性更改的次數
addEntry(hash, k, value, i); //新增新元素,關鍵所在!
return null; //沒有相同的鍵值返回
}
我們把關鍵的方法拿出來分析:
void addEntry(int hash, Object key, Object value, int bucketIndex) {
table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);
因為 hash 的演算法有可能令不同的鍵值有相同的hash碼並有相同的table索引,如:key=“33”和key=Object g的hash都是-8901334,那它經過indexfor之後的索引一定都為i,這樣在new的時候這個Entry的next就會指向這個原本的 table,再有下一個也如此,形成一個連結串列,和put的迴圈對定e.next獲得舊的值。到這裡,HashMap的結構,大家也十分明白了吧?
if (size++ >= threshold) //這個threshold就是能實際容納的量
resize(2 * table.length); //超出這個容量就會將Object table重構
所謂的重構也不神,就是建一個兩倍大的table(我在別的論壇上看到有人說是兩倍加1,把我騙了),然後再一個個indexfor進去!注意!!這就是效率!!如果你能讓你的HashMap不需要重構那麼多次,效率會大大提高!
說到這裡也差不多了,get比put簡單得多,大家,瞭解put,get也差不了多少了。對於collections我是認為,它是適合廣泛的,當不完全適合特有的,如果大家的程式需要特殊的用途,自己寫吧,其實很簡單。(作者是這樣跟我說的,他還建議我用LinkedHashMap,我看了原始碼以後發現,LinkHashMap其實就是繼承HashMap的,然後override相應的方法,有興趣的同人,自己looklook)建個 Object table,寫相應的演算法,就ok啦。
舉個例子吧,像 Vector,list 啊什麼的其實都很簡單,最多就多了的同步的宣告,其實如果要實現像Vector那種,插入,刪除不多的,可以用一個Object table來實現,按索引存取,新增等。
如果插入,刪除比較多的,可以建兩個Object table,然後每個元素用含有next結構的,一個table存,如果要插入到i,但是i已經有元素,用next連起來,然後size++,並在另一個table記錄其位置。
HashMap可謂JDK的一大實用工具,把各個Object對映起來,實現了“鍵--值”對應的快速存取。但實際裡面做了些什麼呢?
在這之前,先介紹一下負載因子和容量的屬性。大家都知道其實一個 HashMap 的實際容量就 因子*容量,其預設值是 16×0.75=12; 這個很重要,對效率很一定影響!當存入HashMap的物件超過這個容量時,HashMap 就會重新構造存取表。這就是一個大問題,我後面慢慢介紹,反正,如果你已經知道你大概要存放多少個物件,最好設為該實際容量的能接受的數字。
兩個關鍵的方法,put和get:
先有這樣一個概念,HashMap是宣告瞭 Map,Cloneable, Serializable 介面,和繼承了 AbstractMap 類,裡面的 Iterator 其實主要都是其內部類HashIterator 和其他幾個 iterator 類實現,當然還有一個很重要的繼承了Map.Entry 的 Entry 內部類,由於大家都有原始碼,大家有興趣可以看看這部分,我主要想說明的是 Entry 內部類。它包含了hash,value,key 和next 這四個屬性,很重要。put的原始碼如下
public Object put(Object key, Object value) {
Object k = maskNull(key);
這個就是判斷鍵值是否為空,並不很深奧,其實如果為空,它會返回一個static Object 作為鍵值,這就是為什麼HashMap允許空鍵值的原因。
int hash = hash(k);
int i = indexFor(hash, table.length);
這連續的兩步就是 HashMap 最牛的地方!研究完我都汗顏了,其中 hash 就是透過 key 這個Object的 hashcode 進行 hash,然後透過 indexFor 獲得在Object table的索引值。
table???不要驚訝,其實HashMap也神不到哪裡去,它就是用 table 來放的。最牛的就是用 hash 能正確的返回索引。其中的hash演算法,我跟JDK的作者 Doug 聯絡過,他建議我看看《The art of programing vol3》可恨的是,我之前就一直在找,我都找不到,他這樣一提,我就更加急了,可惜口袋空空啊!!!
不知道大家有沒有留意 put 其實是一個有返回的方法,它會把相同鍵值的 put 覆蓋掉並返回舊的值!如下方法徹底說明了 HashMap 的結構,其實就是一個表加上在相應位置的Entry的連結串列:
for (Entry e = table; e != null; e = e.next) {
if (e.hash == hash && eq(k, e.key)) {
Object oldvalue = e.value;
e.value = value; //把新的值賦予給對應鍵值。
e.recordAccess(this); //空方法,留待實現
return oldvalue; //返回相同鍵值的對應的舊的值。
}
}
modCount++; //結構性更改的次數
addEntry(hash, k, value, i); //新增新元素,關鍵所在!
return null; //沒有相同的鍵值返回
}
我們把關鍵的方法拿出來分析:
void addEntry(int hash, Object key, Object value, int bucketIndex) {
table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);
因為 hash 的演算法有可能令不同的鍵值有相同的hash碼並有相同的table索引,如:key=“33”和key=Object g的hash都是-8901334,那它經過indexfor之後的索引一定都為i,這樣在new的時候這個Entry的next就會指向這個原本的 table,再有下一個也如此,形成一個連結串列,和put的迴圈對定e.next獲得舊的值。到這裡,HashMap的結構,大家也十分明白了吧?
if (size++ >= threshold) //這個threshold就是能實際容納的量
resize(2 * table.length); //超出這個容量就會將Object table重構
所謂的重構也不神,就是建一個兩倍大的table(我在別的論壇上看到有人說是兩倍加1,把我騙了),然後再一個個indexfor進去!注意!!這就是效率!!如果你能讓你的HashMap不需要重構那麼多次,效率會大大提高!
說到這裡也差不多了,get比put簡單得多,大家,瞭解put,get也差不了多少了。對於collections我是認為,它是適合廣泛的,當不完全適合特有的,如果大家的程式需要特殊的用途,自己寫吧,其實很簡單。(作者是這樣跟我說的,他還建議我用LinkedHashMap,我看了原始碼以後發現,LinkHashMap其實就是繼承HashMap的,然後override相應的方法,有興趣的同人,自己looklook)建個 Object table,寫相應的演算法,就ok啦。
舉個例子吧,像 Vector,list 啊什麼的其實都很簡單,最多就多了的同步的宣告,其實如果要實現像Vector那種,插入,刪除不多的,可以用一個Object table來實現,按索引存取,新增等。
如果插入,刪除比較多的,可以建兩個Object table,然後每個元素用含有next結構的,一個table存,如果要插入到i,但是i已經有元素,用next連起來,然後size++,並在另一個table記錄其位置。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10617731/viewspace-958240/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Java中CompletableFuture的thenApply與thenApplyAsync比較JavaAPP
- TreeMap和HashMap的元素比較HashMap
- HashMap深度分析HashMap
- Go 與 C++ 的對比和比較GoC++
- Java HashMap和Go map原始碼對比JavaHashMapGo原始碼
- Java實體對映工具MapStruct 與BeanUtils效能比較JavaStructBean
- Java中HashMap,LinkedHashMap,TreeMap的區別[轉]JavaHashMap
- Java中CompletableFuture與虛擬執行緒比較Java執行緒
- Java中List集合效能比較Java
- Java 中 Comparable 和 Comparator 比較Java
- JAVA集合:ConcurrentHashMap深度解析(版本對比)JavaHashMap
- HashSet與HashMap比較——新增物件已存在處理方式一樣?HashMap物件
- 雲原生Java與Golang比較 -lgorJavaGolang
- Java JIT與AOT效能比較 - foojayJava
- Mysql中的Btree與Hash索引比較MySql索引
- Kotlin 與 Java 對比KotlinJava
- DDD中事件與命令比較事件
- 【JS 口袋書】第 7 章:JS 中的型別轉換與比較JS型別
- HashMap,LinkedHashMap,TreeMap讀取大量資料效率的比較HashMap
- Easysearch壓縮模式深度比較:ZSTD+source_reuse的優勢分析模式
- Mybatis XML檔案中,對java.util.Date物件的值到轉換為執行SQL進行比較所做的隱式行為分析。MyBatisXMLJava物件SQL
- Java深度拷貝方式和效能對比Java
- Java的BigDecimal比較大小JavaDecimal
- Java鎖與非阻塞演算法的效能比較與分析+原子變數類的應用Java演算法變數
- Java 比較器Java
- pandas中loc、iloc與ix的用法比較
- 區塊鏈中的共識機制分析與對比區塊鏈
- 【原創】InnoDB 和TokuDB的讀寫分析與比較
- Java:HashMap原始碼分析JavaHashMap原始碼
- 微服務中GraphQL與RESTful比較微服務REST
- JAVA中字串比較equals()和equalsIgnoreCase()的區別Java字串
- JAVA中的Comparable介面和自定義比較器Java
- Java中的字串操作(比較String,StringBuiler和StringBuffer)Java字串UI
- Java Optional的orElse()與orElseGet()兩個方法比較 - BaeldungJava
- Java快取機制:Ehcache與Guava Cache的比較Java快取Guava
- volatile與Atomic的比較
- 在表格中基於樹的模型與深度學習優劣對比模型深度學習
- JAVA垃圾回收機制和Python垃圾回收對比與分析JavaPython
- Java陣列轉列表方式對比Java陣列