亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

Java數據結構-HashMap

2019-11-14 20:52:02
字體:
來源:轉載
供稿:網友
java數據結構-HashMap一直以來似乎都有一個錯覺,認為map跟其他的集合類一樣繼承自Collection,其實不然,Map和Collection在結構層次上是沒有任何關系的,通過查看源碼可以發現map所有操作都是基于key-value對,而不是單獨的元素。下面以HashMap為例子,深入對Map的實現機制進行了解,在這個過程中,請打開jdk源碼。Hash算法HashMap使用Hash算法,所以在解剖HashMap之間,需要先簡單的了解Hash算法,Hash算法一般也成為散列算法,通過散列算法將任意的值轉化成固定的長度輸出,該輸出就是散列值,這是一種壓縮映射,也就是,散列值的空間遠遠小于輸入的值空間。簡單的說,hash算法的意義在于提供了一種快速存取數據的方法,它用一種算法建立鍵值與真實值之間的對應關系,(每一個真實值只能有一個鍵值,但是一個鍵值可以對應多個真實值),這樣可以快速在數組等里面存取數據。下面我們建立一個HashMap,然后往里面放入12對key-value,這個HashMap的默認數組長度為16,我們的key分別存放在該數組的格子中,每個格子下面存放的元素又是以鏈表的方式存放元素。publicstaticvoidmain(String[]args){Mapmap=newHashMap();map.put("What","chenyz");map.put("You","chenyz");map.put("Don't","chenyz");map.put("Know","chenyz");map.put("About","chenyz");map.put("Geo","chenyz");map.put("APIs","chenyz");map.put("Can't","chenyz");map.put("Hurt","chenyz");map.put("you","chenyz");map.put("google","chenyz");map.put("map","chenyz");map.put("hello","chenyz");}當我們新添加一個元素時,首先我們通過Hash算法計算出這個元素的Hash值的hashcode,通過這個hashcode的值,我們就可以計算出這個新元素應該存放在這個hash表的哪個格子里面,如果這個格子中已經存在元素,那么就把新的元素加入到已經存在格子元素的鏈表中。運行上面的程序,我們對HashMap源碼進行一點修改,打印出每個key對象的hash值What-->hash值:8You-->hash值:3Don't-->hash值:7Know-->hash值:13About-->hash值:11Geo-->hash值:12APIs-->hash值:1Can't-->hash值:7Hurt-->hash值:1you-->hash值:10google-->hash值:3map-->hash值:8hello-->hash值:0計算出來的Hash值分別代表該key應該存放在Hash表中對應數字的格子中,如果該格子已經有元素存在,那么該key就以鏈表的方式依次放入格子中從上表可以看出,Hash表是線性表和鏈表的綜合所得,根據數據結構的定義,可以得出粗劣的結論,Hash算法的存取速度要比數組差一些,但是比起單純的鏈表,在查找和存取方面卻要好多。如果要查找一個元素時,同樣的方式,通過Hash函數計算出這個元素的Hash值hashcode,然后通過這個hashcode值,直接找到跟這個hash值相對應的線性格子,進如該格子后,對這個格子存放的鏈表元素逐個進行比較,直到找到對應的hash值。在簡單了解完Hash算法后,我們打開HashMap源碼初始化HashMap下面我們看看Map map = new HashMap();這段代碼究竟做了什么,發生了什么數據結構的變化。HashMap中幾個重要的屬性transient Entry[] table;用來保存key-value的對象Entry數組,也就是Hash表transient int size;返回HashMap的鍵值對個數final float loadFactor;負載因子,用來決定Entry數組是否擴容的因子,HashMap默認是0.75fint threshold;重構因子,(capacity * load factor)負載因子與Entry[]數組容積的乘值publicclassHashMap<K,V>extendsAbstractMap<K,V>implementsMap<K,V>,Cloneable,Serializable{intthreshold;finalfloatloadFactor;transientEntry[]table;staticfinalfloatDEFAULT_LOAD_FACTOR=0.75f;staticfinalintDEFAULT_INITIAL_CAPACITY=16;publicHashMap(intinitialCapacity,floatloadFactor){if(initialCapacity<0)thrownewIllegalArgumentException("Illegalinitialcapacity:"+initialCapacity);if(initialCapacity>MAXIMUM_CAPACITY)initialCapacity=MAXIMUM_CAPACITY;if(loadFactor<=0||Float.isNaN(loadFactor))thrownewIllegalArgumentException("Illegalloadfactor:"+loadFactor);//Findapowerof2>=initialCapacityintcapacity=1;while(capacity<initialCapacity)capacity<<=1;this.loadFactor=loadFactor;threshold=(int)(capacity*loadFactor);table=newEntry[capacity];init();}以public HashMap(int initialCapacity, float loadFactor)構造函數為例,另外兩個構造函數實際上也是以同種方式來構建HashMap.首先是要確定hashMap的初始化的長度,這里使用的策略是循環查出一個大于initialCapacity的2的次方的數,例如initialCapacity的值是10,那么大于10的數是2的4次方,也就是16,capacity的值被賦予了16,那么實際上table數組的長度是16,之所以采用這樣的策略來構建Hash表的長度,是因為2的次方運算對于計算機來說是有相當的效率。loadFactor,被稱為負載因子,HashMap的默認負載因子是0.75fthreshold,接下來是重構因子,由負載因子和容量的乘機組成,它表示當HashMap元素被存放了多少個之后,需要對HashMap進行重構。通過這一系列的計算和定義后,初始化Entry[] table;put(key,value)接下來看一對key-value是如何被存放到HashMap中:put(key,value)publicVput(Kkey,Vvalue){if(key==null)returnputForNullKey(value);inthash=hash(key.hashCode());inti=indexFor(hash,table.length);System.out.access(this);returnoldValue;}}modCount++;addEntry(hash,key,value,i);returnnull;}staticinthash(inth){h^=(h>>>20)^(h>>>12);returnh^(h>>>7)^(h>>>4);}staticintindexFor(inth,intlength){returnh&(length-1);}這里是整個hash的關鍵,請打開源碼查看一步一步查看。hash(key.hashCode())計算出key的hash碼 //對于hash()的算法,這里有一篇分析很透徹的文章<HashMap hash方法分析>indexFor(hash, table.length)通過一個與算法計算出來,該key應在存放在Hash表的哪個格子中。for (Entry<K,V> e = table[i]; e != null; e = e.next)然后再遍歷table[i]格中的鏈表,判斷是否已經存在一樣的key,如果存在一樣的key值,那么就用新的value覆蓋舊的value,并把舊的value值返回。addEntry(hash, key, value, i)如果經過遍歷鏈表沒有發現同樣的key,那么進行addEntry函數的操作,增加當前key到hash表中的第i個格子中的鏈表中voidaddEntry(inthash,Kkey,Vvalue,intbucketIndex){Entry<K,V>e=table[bucketIndex];table[bucketIndex]=newEntry<K,V>(hash,key,value,e);if(size++>=threshold)resize(2*table.length);}Entry<K,V> e = table[bucketIndex]; 創建一個Entry對象來存放鍵值(ps:Entry對象是一個鏈表對象)table[bucketIndex] = new Entry<K,V>(hash, key, value, e);將Entry對象添加到鏈表中if (size++ >= threshold) resize(2 * table.length);最后將size進行自增,判斷size值是否大于重構因子,如果大于那么就是用resize進行擴容重構。voidresize(intnewCapacity){Entry[]oldTable=table;intoldCapacity=oldTable.length;if(oldCapacity==MAXIMUM_CAPACITY){threshold=Integer.MAX_VALUE;return;}Entry[]newTable=newEntry[newCapacity];transfer(newTable);table=newTable;threshold=(int)(newCapacity*loadFactor);}這里為什么是否需要擴容重構,其實是涉及到負載因子的性能問題loadFactor負載因子上面說過loadFactor是一個hashMap的決定性屬性,HashSet和HashMap的默認負載因子都是0.75,它表示,如果哈希表的容量超過3/4時,將自動成倍的增加哈希表的容量,這個值是權衡了時間和空間的成本,如果負載因子較高,雖然會減少對內存空間的需求,但也會增加查找數據的時間開銷,無論是put()和get()都涉及到對數據進行查找的動作,所以負載因子是不適宜設置過高get(key)接下來看看get(key)做了什么publicVget(Objectkey){if(key==null)returngetForNullKey();inthash=hash(key.hashCode());for(Entry<K,V>e=table[indexFor(hash,table.length)];e!=null;e=e.next){Objectk;if(e.hash==hash&&((k=e.key)==key||key.equals(k)))returne.value;}returnnull;}這些動作似乎是跟put(key,value)相識,通過hash算法獲取key的hash碼,再通過indexFor定位出該key存在于table的哪一個下表,獲取該下標然后對下標中的鏈表進行遍歷比對,如果有符合就直接返回該key的value值。keySet()這里還涉及另一個問題,上面說了HashMap是跟set沒有任何親屬關系,但map也一樣實現了keySet接口,下面譜析一下keySet在hashMap中是如何實現的,這里給出部分代碼,請結合源碼查看publicKnext(){returnnextEntry().getKey();}finalEntry<K,V>nextEntry(){if(modCount!=expectedModCount)thrownewConcurrentModificationException();Entry<K,V>e=next;if(e==null)thrownewNoSuchElementException();if((next=e.next)==null){Entry[]t=table;while(index<t.length&&(next=t[index++])==null);}current=e;returne;}代碼很簡單,就是對每個格子里面的鏈表進行遍歷,也正是這個原因,當我們依次將key值put進hashMap中,但在使用map.entrySet().iterator()進行遍歷時候卻不是put時候的順序。擴容在前面說到put函數的時候,已經提過了擴容的問題if(size++>=threshold)resize(2*table.length);這里一個是否擴容的判斷,當數據達到了threshold所謂的重構因子,而不是HashMap的最大容量,就進行擴容。voidresize(intnewCapacity){Entry[]oldTable=table;intoldCapacity=oldTable.length;if(oldCapacity==MAXIMUM_CAPACITY){threshold=Integer.MAX_VALUE;return;}Entry[]newTable=newEntry[newCapacity];transfer(newTable);table=newTable;threshold=(int)(newCapacity*loadFactor);}voidtransfer(Entry[]newTable){Entry[]src=table;intnewCapacity=newTable.length;for(intj=0;j<src.length;j++){Entry<K,V>e=src[j];if(e!=null){src[j]=null;do{Entry<K,V>next=e.next;inti=indexFor(e.hash,newCapacity);e.next=newTable[i];newTable[i]=e;e=next;}while(e!=null);}}}transfer方法實際上是將所有的元素重新進行一些hash,這是因為容量變化了,每個元素相對應的hash值也會不一樣。使用HashMap1.不要再高并發中使用HashMap,HashMap是線程不安全,如果被多個線程共享之后,將可能發生不可預知的問題。2.如果數據大小事固定的,最好在初始化的時候就給HashMap一個合理的容量值,如果使用new HashMap()默認構造函數,重構因子的值是16*0.75=12,當HashMap的容量超過了12后,就會進行一系列的擴容運算,重建一個原來成倍的數組,并且對原來存在的元素進行重新的hash運算,如果你的數據是有成千上萬的,那么你的成千上萬的數據也要跟這你的擴容不斷的hash,這將產生高額的內存和cpu的大量開銷。當然啦,HashMap的函數還有很多,不過都是基于table的鏈表進行操作,當然也就是hash算法,Map & hashMap在平時我們的應用非常多,最重要的是我們要對每句代碼中每塊數據結構變化心中有數。上面主要是參考了jdk源碼,數據結構和一些相關資料本著好記性不如爛博客的精神記錄下來,希望朋友們如果發覺哪里不對請指出來,虛心請教
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久免费视频观看| 成人a视频在线观看| 一个人www欧美| 国产欧美日韩综合精品| 综合欧美国产视频二区| 国产精品999999| www.欧美精品一二三区| 欧美日韩国产精品一区二区不卡中文| 成人黄色激情网| 91久久久久久国产精品| 国产精品69av| 欧美国产日韩一区二区三区| 国产精品免费电影| 日韩视频欧美视频| 久久久噜噜噜久噜久久| 国产激情综合五月久久| 欧美在线视频播放| 国产精品专区h在线观看| 国产精品久久久久久久天堂| 日韩av一卡二卡| 裸体女人亚洲精品一区| 日韩免费黄色av| 欧美丰满少妇xxxxx做受| 成人亚洲激情网| 亚洲人成网站色ww在线| 欧美精品福利在线| 欧美日韩国产123| 国产成人在线亚洲欧美| 亚州国产精品久久久| 日韩精品一区二区视频| 欧美国产日本高清在线| 国产精品久久久久久中文字| 亚洲欧洲日产国码av系列天堂| 日韩国产高清污视频在线观看| 久久久久九九九九| 欧美专区福利在线| 精品一区二区三区四区在线| 午夜精品一区二区三区在线播放| 上原亚衣av一区二区三区| 国内揄拍国内精品少妇国语| 久久久精品日本| 中文精品99久久国产香蕉| 久久久在线视频| 一区二区三区在线播放欧美| 亚洲午夜小视频| 成人高清视频观看www| 亚洲天天在线日亚洲洲精| 久久夜色撩人精品| 欧美色另类天堂2015| 奇米影视亚洲狠狠色| 国产精品日韩在线| 韩国精品久久久999| 久久久久久中文字幕| 日韩av日韩在线观看| 欧美日本亚洲视频| 欧美激情va永久在线播放| 国产69精品久久久久9| 国产精品69久久久久| 久久久电影免费观看完整版| 日韩欧美在线视频观看| 国产精品国产自产拍高清av水多| 国产一区二区三区在线观看网站| 欧美大尺度电影在线观看| 欧美激情网友自拍| 国自在线精品视频| 亚洲在线第一页| 久久久久久久香蕉网| 日韩免费av一区二区| 久久精品99久久久久久久久| 精品免费在线视频| 亚洲黄页网在线观看| 久久国产精彩视频| 尤物精品国产第一福利三区| 91免费的视频在线播放| 91日本在线观看| 国产精品入口免费视| 亚洲精品国产品国语在线| 中文字幕亚洲精品| 欧美国产日本在线| 国产精品色婷婷视频| 国产美女91呻吟求| 国产精品69精品一区二区三区| 色婷婷综合久久久久中文字幕1| 欧美日韩在线一区| 成人久久久久久| 亚洲日本欧美日韩高观看| 亚洲自拍另类欧美丝袜| 欧洲亚洲免费在线| 久久久亚洲成人| 日韩动漫免费观看电视剧高清| 国产精品自产拍高潮在线观看| 欧美一级大片在线免费观看| 日韩免费在线电影| 亚洲天天在线日亚洲洲精| 国产z一区二区三区| 欧美成人国产va精品日本一级| 欧美丰满少妇xxxxx| 九九热99久久久国产盗摄| 欧美大荫蒂xxx| 91亚洲精品在线观看| 欧美日韩激情视频| 神马久久桃色视频| 两个人的视频www国产精品| 午夜精品蜜臀一区二区三区免费| 精品高清美女精品国产区| 中文字幕亚洲综合久久| 尤物yw午夜国产精品视频| 日韩电影视频免费| 久久国产精品偷| 国产精品福利网站| 亚洲精品中文字| 亚洲精品v天堂中文字幕| 高清欧美电影在线| 色婷婷亚洲mv天堂mv在影片| 日韩精品欧美激情| 欧美日韩亚洲高清| 欧美激情视频网站| 91亚洲精品在线观看| 国产精品爽黄69天堂a| 日韩在线视频免费观看高清中文| 精品国产31久久久久久| 国产69久久精品成人| 亚洲欧美日韩国产成人| 国产成人一区二区三区小说| 激情懂色av一区av二区av| 中文字幕亚洲欧美在线| 亚洲黄色成人网| 久久久久国色av免费观看性色| 97视频在线观看视频免费视频| 久久夜色精品国产亚洲aⅴ| 国产欧美最新羞羞视频在线观看| 精品一区二区三区四区在线| 77777少妇光屁股久久一区| 亚洲黄色片网站| 欧美性猛交xxxx乱大交3| 亚洲欧美日本另类| 在线日韩中文字幕| 国产精品亚洲一区二区三区| 国产精品久久久久久久久久ktv| 日本高清久久天堂| 亚洲最大av网站| 国产大片精品免费永久看nba| 亚洲一区二区久久久| 国产精品白嫩美女在线观看| 亚洲午夜av久久乱码| 欧美激情日韩图片| 在线视频欧美日韩| 国产精品一区二区三区在线播放| 国产激情综合五月久久| 国产成人鲁鲁免费视频a| 最近2019好看的中文字幕免费| 欧美一级淫片videoshd| 欧美日韩免费看| 国内精品免费午夜毛片| 成人激情视频在线| 欧美日本国产在线| 欧美小视频在线| 国产精品久久久久久影视| 亚洲图片欧美午夜| 欧美日韩精品在线观看| 日韩欧美在线视频日韩欧美在线视频| 日本sm极度另类视频| 激情成人中文字幕| 一区二区国产精品视频|