import java.util.Map; import java.util.Objects; public class MyHashMap { // åå§é»è®¤çæ°ç»å®¹é static final int INIT_CAPACITY = 1<<4; //æ°ç»æå¤§ç容é,å 为 æ°ç»è®¾ç½®ä¸º 2çæ´æ¬¡æ¹åï¼è 31 次æ¹ä¸ºè´æ°ï¼æä»¥æå¤§åªè½ä¸º 1 << 30 static final int MAX_CAPACITY = 1<<30; // é»è®¤çè£ å¡«å å static final float DEFAULT_LOADFACTOR = 0.75f; /** * Entry ç±» 为mapä¸åºæ¬çåå * * key 为é®ï¼value ä¸ºå¼ * next æ¯å¨åå¸å²çªæ¶ï¼æåçä¸ä¸ä¸ª Entry * h ä¸ºä¼ å ¥çhashå¼ */ static class Entry{ Object key; private Object value; private Entry next; int h; public Entry(int h,Object key, Object value, Entry next) { this.key = key; this.value = value; this.next = next; this.h = h; } public Entry() { } public Object getKey() { return key; } public Object getValue() { return value; } public Object setValue(Object newValue) { Object oldValue=value; this.value = newValue; return oldValue; } public Entry getNext() { return next; } public void setNext(Entry next) { this.next = next; } public final int hashcode(){ return Objects.hashCode(key)^Objects.hashCode(value); } } // è¿å hashå¼ ï¼å¦æ key 为 nullï¼è¿å 0 // éæ°è®¡ç®çåå¸å¼ä¸º 忥çhashCode å é«å «ä½è¿è¡ç¸ä¸ï¼ // è¿æ ·åç好å¤ï¼ 妿ä¸è¿æ ·ï¼å½æ°ç»è¾ççæ¶åï¼åªè½æä½ä½è¿è¡è¿ç®ï¼é«ä½æ æï¼åå¸ä¸å static final int hash(Object key){ int h; return (key==null) ? 0 : (h=key.hashCode())^(h >>> 16); } // æ°ç»é¿åº¦ï¼éè¿è¿ä¸ªè¿ç®å¾å° // æ©å®¹åç大尿»æ¯ 2ç n 次æ¹åï¼ä¸æ¯ç¦»æªæ©å®¹åçæ°åæè¿çé£ä¸ª 2çnæ¬¡æ¹ static final int tableSizeFor(int cap) { int n = cap - 1; n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16; return (n < 0) ? 1 : (n >= MAX_CAPACITY) ? MAX_CAPACITY : n + 1; } Entry[] table; // table æ¡¶ä¸ç个æ°--æ°ç»ç大å°; int size; // ä¿®æ¹æ¬¡æ° int modCount; // æ©å®¹çéå¼, capacity * load factor int threshold; // è£ å¡«å å float loadFactor; public MyHashMap(int initCapacity,float loadFactor) { if(initCapacity<0) throw new IllegalArgumentException("åå§å容é失败: "+ initCapacity); if(initCapacity>= MAX_CAPACITY) initCapacity= MAX_CAPACITY; if(loadFactor<=0||Float.isNaN(loadFactor)) throw new IllegalArgumentException("è£ å¡«å åä¸åæ³"+ loadFactor); this.loadFactor=loadFactor; this.threshold=tableSizeFor(initCapacity); } /** * åªä¼ å ¥æ°ç»å®¹é大å°ç * @param initCapacity */ public MyHashMap(int initCapacity) { this(initCapacity,DEFAULT_LOADFACTOR); } /** * æ åçï¼å ¨é¨é»è®¤ */ public MyHashMap() { this.loadFactor=DEFAULT_LOADFACTOR; } public MyHashMap(Map m){ this.loadFactor=DEFAULT_LOADFACTOR; } /** * å½keyæ¯Mapçæ¶åæè°ç¨ * @param m * @param evict */ final void putEntries(Map m,boolean evict){ int s=m.size(); if(s>0){ if(table==null){ float ft=(float) s/loadFactor+1.0f; int t=(ft<(float) MAX_CAPACITY)?(int)ft: MAX_CAPACITY; // 妿 忥ç hashmapç table ç大å°ä¸º0ï¼ç¬¬ä¸æ¬¡æ·»å æ°åï¼ï¼ // é£ä¹å°±å°ä»æç §ç°å¨ç æ·»å çtable 大å°è¿è¡æ©å®¹ if(t>threshold){ threshold=tableSizeFor(t); } } // å¦ææ·»å ç entries ç table 大å°å¤§äº éå¼ï¼å°±æ©å®¹ else if(s>threshold){ resize(); } // æå个 Entry æ¾å ¥å° mapä¸ for( Object e : m.entrySet()){ Object key = ((Map.Entry) e).getKey(); Object value = ((Map.Entry) e).getValue(); putVal(hash(key),key,value,false,evict); } } } /** * åæ¾å个å ç´ æ¶è°ç¨ */ public Object put(Object key,Object value){ return putVal(hash(key),key,value,false,true); } /** * åæ¾å个å ç´ çæä½ * @param hash åå¸å¼ï¼ç±keyçé«16ä½åä½16ä½å¼æå¾å° * @param key é® * @param value å¼ * @param onlyIfAbsent * @param evict * @return */ private Object putVal(int hash, Object key, Object value, boolean onlyIfAbsent, boolean evict) { Entry[] tab; Entry p; int n,i; // å¦æç¬¬ä¸æ¬¡ è¿è¡åæ¾æ°æ®ï¼è¿è¡åå§åï¼table 被延è¿å°è¿è¡æ°æ®åæ¾æ¶æåå§å if((tab = table) == null || (n = table.length)==0){ n = (tab = resize()).length; } // 妿table ç´¢å¼ä¸æ²¡æåæ¾å ç´ if((p = table[i = ((n - 1) & hash)]) == null){ tab[i] = newEntry(hash,key,value,null); } else { Entry e; Object k; // 妿 key ç¸åï¼é£ä¹å°±ç´æ¥å° value è¦ç // 为ä»ä¹è¦æ¯è¾è¿ä¹å¤æ¬¡ // 1.é¦å 夿 åå¸å¼æ¯å¦ç¸å if(p.h == hash && // 2.å¤æä¸¤ä¸ªkeyæ¯å¦ç¸çï¼ä½¿ç¨ '==' æ¯éå符串æ åµï¼ä¹æ¯è¾ä¸¤ä¸ªçå 容ï¼ä½¿ç¨'equals' æ¯é对å符串 (((k = p.key) == key) || (key != null && key.equals(k)))) // è¦çvalueå¼ e = p; // è¿ä¸ªæ¯æ çæ åµ //else if(p instance of TreeNode) // é¾ else{ for(int binCount=0;;++binCount){ // éåå°æåï¼æå ¥ if((e = p.next) == null){ p.next = newEntry(hash,key,value,null); /* 妿 binCount > è½¬åæ çéå¼ ,åå°é¾è¡¨è½¬å为æ if(binCount >= TREEIFY_THRESHOLD-1) treeifyBin(tab,hash); */ break; } if(p.h == hash && (((k = p.key) == key) || (key != null && key.equals(k)))) break; // ç§»å¨å°ä¸ä¸ä¸ª p = e; } // 妿æç¸åºçæ å° if(e != null){ Object oldValue = e.value; if(!onlyIfAbsent || oldValue == null) e.value = value; return oldValue; } } } // ä¿®æ¹æ¬¡æ° ++ ++ modCount; // 大äºéå¼å°±æ©å®¹ if(++size >threshold) resize(); // æå ¥å ç´ //afterNodeInsertion(evict); return null; } public Object get(Object key){ Entry e; return (e = getNode(hash(key),key)) == null ? null : e.value; } /** * æ¥æ¾é®å¼å¯¹ * @param hash * @param key * @return æ¥æ¾çå ç´ æè null */ private Entry getNode(int hash, Object key) { Entry[] tab; Entry first,e; int n; Object k; if((tab=table) != null && (n=tab.length) >0 && (first = tab[(n-1) & hash]) !=null){ // æ£æ¥é¾è¡¨ä¸ç第ä¸ä¸ª if (first.h == hash && (((k = first.key) == key ) || (key !=null && key.equals(k)))){ return first; } if ((e = first.next) != null){ // 夿æ¯å¦ä¸ºæ å½¢ç»æ // if (e instanceof TreeNode) // ç¶åè°ç¨ TreeNodeçæç´¢æ¹æ³ //è¿ä¸ªæ¯å¨é¾è¡¨ä¸æ¥æ¾å ç´ do{ if (e.h == hash && (((k = e.key) == key) || (key !=null && key.equals(k)))) return e; }while ((e = e.next) != null); } } return null; } /** * map䏿¯å¦æè¿ä¸ª é® * @param key * @return false or true */ public boolean containsKey(Object key){ return getNode(hash(key),key) != null; } private Entry newEntry(int hash, Object key, Object value, Entry entry) { return new Entry(hash,key,value,entry); } /** * æ©å®¹å½æ° * ä½¿ç¨æ æ¯: 1.åå§åæ°ç»tableï¼ * 2.++size>threshold. * @return æ°çæ°ç»table * æè æ¯æ©å®¹ä¸åé¿åº¦è¶ è¿æå¤§å¼ï¼è¿å忥çtableæ°ç» */ final Entry[] resize() { // å®ä¹æ§çæ°ç»ä¸º Entry ç±»åçæ°ç»ï¼oldTab Entry[] oldTab = table; // 妿oldTab==null åè¿å 0ï¼å¦åè¿åæ°ç»å¤§å° int oldCap = (oldTab==null) ? 0 : oldTab.length; int oldThreshold = threshold; int newCap,newThreshold=0; // 说æå·²ç»ä¸æ¯ç¬¬ä¸æ¬¡ æ©å®¹ï¼é£ä¹å·²ç»åå§åè¿ï¼å®¹éä¸å®æ¯ 2çn次æ¹ï¼æä»¥å¯ä»¥ç´æ¥ä½è¿ç® if(oldCap>0){ // 妿 åæ¥çæ°ç»å¤§å°å·²ç»å¤§äºçäºäºæå¤§å¼ï¼é£ä¹éå¼è®¾ç½®ä¸º Integerçæå¤§å¼,å³ä¸ä¼åè¿è¡æ©å®¹ if(oldCap >= MAX_CAPACITY){ threshold = Integer.MAX_VALUE; return oldTab; } // å æ¤å·²ç»ä¸æ¯ç¬¬ä¸æ¬¡æ©å®¹ï¼ä¸å®æ¯2çnæ¬¡æ¹ else if ((newCap = oldCap << 1) < MAX_CAPACITY && oldCap >= INIT_CAPACITY) newThreshold = oldThreshold << 1; } // 妿oldThreshold > 0,å¹¶ä¸oldCap == 0ï¼è¯´ææ¯è¿æ²¡æè¿è¡è°ç¨resizeæ¹æ³ã // 说æè¾å ¥äºåå§å¼ï¼ä¸oldThreshold为 æ¯è¾å ¥å¼å¤§çæå°ç2çnæ¬¡æ¹ // é£ä¹å°±æ oldThreshold çå¼èµç» newCap ï¼å 为è¿ä¸ªå¼ç°å¨ä¸º æ¯è¾å ¥å¼å¤§çæå°ç2çnæ¬¡æ¹ else if(oldThreshold>0) newCap = oldThreshold; // è¿ä¸ªæ¯åªæä½¿ç¨æ åæé å¨çæ¶åæè½æ»¡è¶³çæ¡ä»¶ãï¼å ¨é¨æ¯å¦é»è®¤çå¼ else{ newCap = INIT_CAPACITY; newThreshold = (int) (INIT_CAPACITY * DEFAULT_LOADFACTOR); } // if(newThreshold == 0){ float ft = (float) (newCap * loadFactor); newThreshold =(newCap < MAX_CAPACITY && ft < (float) MAX_CAPACITY ? (int )ft : Integer.MAX_VALUE); } threshold = newThreshold; Entry newTable[] = new Entry[newCap]; table=newTable; // å°åæ¥æ°ç»ä¸çææå ç´ é½ copyè¿æ°çæ°ç» if(oldTab != null){ for (int j = 0; j < oldCap; j++) { Entry e; if((e = oldTab[j]) != null){ oldTab[j] = null; // 说æè¿æ²¡ææé¾ï¼æ°ç»ä¸åªæä¸ä¸ª if(e.next == null){ // éæ°è®¡ç® æ°ç»ç´¢å¼ å¼ newTable[e.h & (newCap-1)] = e; } // 夿æ¯å¦ä¸ºæ ç»æ //else if (e instanceof TreeNode) // 妿䏿¯æ ï¼åªæ¯é¾è¡¨,å³é¿åº¦è¿æ²¡æå¤§äº 8 è¿åææ else{ // æ©å®¹åï¼å¦æå ç´ ç index è¿æ¯åæ¥çã就使ç¨è¿ä¸ªloåç¼ç Entry loHead=null, loTail =null; // æ©å®¹å å ç´ indexæ¹åï¼é£ä¹å°±ä½¿ç¨ hiåç¼å¼å¤´ç Entry hiHead = null, hiTail = null; Entry next; do { next = e.next; //è¿ä¸ªé常éè¦ï¼ä¹æ¯è¾é¾æï¼ // å°å®å忥çé¿åº¦è¿è¡ç¸ä¸ï¼å°±æ¯å¤æä»ç忥çhashçä¸ä¸ä¸ª bit 使¯å¦ä¸º 1ã //ä»¥æ¤æ¥å¤æä»æ¯å¨ç¸åçç´¢å¼è¿æ¯tableé¿åº¦å ä¸åæ¥çç´¢å¼ if((e.h & oldCap) == 0){ // 妿 loTail == null ,说æè¿ä¸ª ä½ç½®ä¸æ¯ç¬¬ä¸æ¬¡æ·»å ï¼æ²¡æåå¸å²çª if(loTail == null) loHead = e; else loTail.next = e; loTail = e; } else{ if(hiTail == null) loHead = e; else hiTail.next = e; hiTail = e ; } }while ((e = next) != null); if(loTail != null){ loTail.next = null; newTable[j] = loHead; } // æ°çindex çäºåæ¥ç index+oldCap else { hiTail.next = null; newTable[j+oldCap] = hiHead; } } } } } return newTable; } }