成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Java 8測(cè)試使用:HashMap的性能提升

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

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

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

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

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

  1. class Key implements Comparable<Key> { 
  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. public static Key of(int value) { 
  9. return KEYS_CACHE[value]; 

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

  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<Key, Integer> 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. public void timeMapGet(int reps) { 
  14. for (int i = 0; i < reps; i++) { 
  15. map.get(Keys.of(i % mapSize)); 

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

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

  1. class Key implements Comparable<Key> { 
  2. //... 
  3. @Override 
  4. public int hashCode() { 
  5. return 0

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

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

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

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

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

原文轉(zhuǎn)自:Java 8:HashMap的性能提升

 
責(zé)任編輯:張偉 來(lái)源: Java譯站
相關(guān)推薦

2014-04-28 10:17:01

2023-05-12 13:21:12

JMHJava程序

2011-07-21 10:42:26

2015-02-05 09:47:52

Web性能Web開(kāi)發(fā)

2021-09-27 08:16:38

Webpack 前端Cache

2022-07-10 22:29:42

AtomicJDK項(xiàng)目

2013-09-11 16:11:57

C++StringBuild

2017-01-10 14:08:33

C++StringBuild性能

2011-09-16 10:19:41

2009-03-30 09:10:53

微軟瀏覽器IE8

2015-11-10 09:25:05

HTTP2提升性能

2023-03-01 15:14:48

數(shù)據(jù)集機(jī)器學(xué)習(xí)

2009-04-20 08:59:49

Firefox性能測(cè)試瀏覽器

2015-01-21 15:40:44

GoRuby

2009-10-19 10:26:39

2013-05-22 09:38:03

GoGo語(yǔ)言Go性能

2017-03-13 09:50:00

HadoopHive

2014-07-31 09:28:09

ASP.NETWeb API

2012-03-12 13:54:56

ASP.NET

2015-02-04 09:19:03

Web優(yōu)化
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 天堂精品 | 久久精品国产亚洲 | 亚洲精品视频一区二区三区 | 成人免费视频 | 久久精品视频91 | 成年人网站免费 | 久久国产精品视频 | 国产精品久久久 | 欧美xxxx做受欧美 | 精品天堂| 99这里只有精品视频 | 亚洲国产成人精品一区二区 | 天天曰夜夜 | 成人h视频| 欧美三级成人理伦 | 国产高清在线精品 | 自拍中文字幕 | 成人小视频在线观看 | 成人免费视频观看 | 欧美日韩在线综合 | 麻豆久久久久久 | 91久久国产综合久久 | 亚洲欧美在线一区 | 能免费看的av | 日韩区| 1204国产成人精品视频 | 亚洲国产精品人人爽夜夜爽 | av天天看| 精品国产精品一区二区夜夜嗨 | 看片一区 | 国产精品久久久久久久久久免费看 | 国产视频精品在线观看 | 欧美精品一区二区在线观看 | 亚洲精品在线视频 | 9porny九色视频自拍 | 久久免费资源 | 午夜精品久久久久久久星辰影院 | 国产丝袜一区二区三区免费视频 | 久久免费精品视频 | 亚洲一区二区成人 | 少妇一级淫片免费播放 |