日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Java8中HashMap的性能提升

HashMap是一個高效通用的數(shù)據(jù)結(jié)構(gòu),它在每一個Java程序中都隨處可見。先來介紹些基礎(chǔ)知識。你可能也知 道,HashMap使用key的hashCode()和equals()方法來將值劃分到不同的桶里。桶的數(shù)量通常要比map中的記錄的數(shù)量要稍大,這樣 每個桶包括的值會比較少(最好是一個)。當(dāng)通過key進(jìn)行查找時,我們可以在常數(shù)時間內(nèi)迅速定位到某個桶(使用hashCode()對桶的數(shù)量進(jìn)行取模) 以及要找的對象。

這些東西你應(yīng)該都已經(jīng)知道了。你可能還知道哈希碰撞會對hashMap的性能帶來災(zāi)難性的影響。如果多個hashCode()的值落到同一個桶內(nèi)的 時候,這些值是存儲到一個鏈表中的。最壞的情況下,所有的key都映射到同一個桶中,這樣hashmap就退化成了一個鏈表——查找時間從O(1)到 O(n)。我們先來測試下正常情況下hashmap在Java 7和Java 8中的表現(xiàn)。為了能完成控制hashCode()方法的行為,我們定義了如下的一個Key類:

 
 
  1. class Key implements Comparable {
  2. private final int value;
  3. Key(int value) {
  4. this.value = value;
  5. }
  6. @Override
  7. public int compareTo(Key o) {
  8. return Integer.compare(this.value, o.value);
  9. }
  10. @Override
  11. public boolean equals(Object o) {
  12. if (this == o) return true;
  13. if (o == null || getClass() != o.getClass())
  14. return false;
  15. Key key = (Key) o;
  16. return value == key.value;
  17. }
  18. @Override
  19. public int hashCode() {
  20. return value;
  21. }
  22. }

Key類的實(shí)現(xiàn)中規(guī)中矩:它重寫了equals()方法并且提供了一個還算過得去的hashCode()方法。為了避免過度的GC,我將不可變的Key對象緩存了起來,而不是每次都重新開始創(chuàng)建一遍:

 
 
  1. class Key implements Comparable {
  2. public class Keys {
  3. public static final int MAX_KEY = 10_000_000;
  4. private static final Key[] KEYS_CACHE = new Key[MAX_KEY];
  5. static {
  6. for (int i = 0; i < MAX_KEY; ++i) {
  7. KEYS_CACHE[i] = new Key(i);
  8. }
  9. }
  10. public static Key of(int value) {
  11. return KEYS_CACHE[value];
  12. }
  13. }

現(xiàn)在我們可以開始進(jìn)行測試了。我們的基準(zhǔn)測試使用連續(xù)的Key值來創(chuàng)建了不同的大小的HashMap(10的乘方,從1到1百萬)。在測試中我們還會使用key來進(jìn)行查找,并測量不同大小的HashMap所花費(fèi)的時間:

 
 
  1. import com.google.caliper.Param;
  2. import com.google.caliper.Runner;
  3. import com.google.caliper.SimpleBenchmark;
  4. public class MapBenchmark extends SimpleBenchmark {
  5. private HashMap map;
  6. @Param
  7. private int mapSize;
  8. @Override
  9. protected void setUp() throws Exception {
  10. map = new HashMap<>(mapSize);
  11. for (int i = 0; i < mapSize; ++i) {
  12. map.put(Keys.of(i), i);
  13. }
  14. }
  15. public void timeMapGet(int reps) {
  16. for (int i = 0; i < reps; i++) {
  17. map.get(Keys.of(i % mapSize));
  18. }
  19. }
  20. }

有意思的是這個簡單的HashMap.get()里面,Java 8比Java 7要快20%。整體的性能也相當(dāng)不錯:盡管HashMap里有一百萬條記錄,單個查詢也只花了不到10納秒,也就是大概我機(jī)器上的大概20個CPU周期。 相當(dāng)令人震撼!不過這并不是我們想要測量的目標(biāo)。

假設(shè)有一個很差勁的key,他總是返回同一個值。這是最糟糕的場景了,這種情況完全就不應(yīng)該使用HashMap:

 
 
  1. class Key implements Comparable {
  2. //...
  3. @Override
  4. public int hashCode() {
  5. return 0;
  6. }
  7. }

Java 7的結(jié)果是預(yù)料中的。隨著HashMap的大小的增長,get()方法的開銷也越來越大。由于所有的記錄都在同一個桶里的超長鏈表內(nèi),平均查詢一條記錄就需要遍歷一半的列表。因此從圖上可以看到,它的時間復(fù)雜度是O(n)。

不過Java 8的表現(xiàn)要好許多!它是一個log的曲線,因此它的性能要好上好幾個數(shù)量級。盡管有嚴(yán)重的哈希碰撞,已是最壞的情況了,但這個同樣的基準(zhǔn)測試在JDK8中的時間復(fù)雜度是O(logn)。單獨(dú)來看JDK 8的曲線的話會更清楚,這是一個對數(shù)線性分布:

為什么會有這么大的性能提升,盡管這里用的是大O符號(大O描述的是漸近上界)?其實(shí)這個優(yōu)化在JEP-180中已經(jīng)提到了。如果某個桶中的記錄過 大的話(當(dāng)前是TREEIFY_THRESHOLD = 8),HashMap會動態(tài)的使用一個專門的treemap實(shí)現(xiàn)來替換掉它。這樣做的結(jié)果會更好,是O(logn),而不是糟糕的O(n)。它是如何工作 的?前面產(chǎn)生沖突的那些KEY對應(yīng)的記錄只是簡單的追加到一個鏈表后面,這些記錄只能通過遍歷來進(jìn)行查找。但是超過這個閾值后HashMap開始將列表升 級成一個二叉樹,使用哈希值作為樹的分支變量,如果兩個哈希值不等,但指向同一個桶的話,較大的那個會插入到右子樹里。如果哈希值相等,HashMap希 望key值最好是實(shí)現(xiàn)了Comparable接口的,這樣它可以按照順序來進(jìn)行插入。這對HashMap的key來說并不是必須的,不過如果實(shí)現(xiàn)了當(dāng)然最 好。如果沒有實(shí)現(xiàn)這個接口,在出現(xiàn)嚴(yán)重的哈希碰撞的時候,你就并別指望能獲得性能提升了。

這個性能提升有什么用處?比方說惡意的程序,如果它知道我們用的是哈希算法,它可能會發(fā)送大量的請求,導(dǎo)致產(chǎn)生嚴(yán)重的哈希碰撞。然后不停的訪問這些 key就能顯著的影響服務(wù)器的性能,這樣就形成了一次拒絕服務(wù)攻擊(DoS)。JDK 8中從O(n)到O(logn)的飛躍,可以有效地防止類似的攻擊,同時也讓HashMap性能的可預(yù)測性稍微增強(qiáng)了一些。我希望這個提升能最終說服你的 老大同意升級到JDK 8來。

測試使用的環(huán)境是:Intel Core i7-3635QM @ 2.4 GHz,8GB內(nèi)存,SSD硬盤,使用默認(rèn)的JVM參數(shù),運(yùn)行在64位的Windows 8.1系統(tǒng) 上。

原文出處: Tomasz Nurkiewicz   譯文出處: deepinmind


分享標(biāo)題:Java8中HashMap的性能提升
轉(zhuǎn)載源于:http://www.dlmjj.cn/article/dhhgcdh.html