手寫實現HashMap程式碼
阿新 • • 發佈:2018-12-23
瞭解HashMap底層=陣列+連結串列
HashMap底層原始碼通過 連結串列法 來解決hash衝突,找到hash值對應位置不為空,維護一個連結串列
ThreadLocal底層原始碼,ThreadLocalMap中通過 線性探測 解決hash衝突,找到hash值對應位置不為空,依次向後找不為空為止
public interface DIYMap<K,V> { //Map雙列集合 基本功能是 快速取 public V put(K k,V v); //快速取 public V get(K k); //定義一個內部介面 public interface Entry<K,V>{ public K getKey(); public V getValue(); } }
import java.util.ArrayList; import java.util.List; /* 瞭解hashmap中entry實體的結構 crc16演算法 hashmap底層=陣列+連結串列 通過hash演算法帶來的好處, 快存快取 / 陣列在存的時候是需要遍歷的 HashMap底層是怎麼回事? */ public class DIYHashMap<K, V> implements DIYMap<K, V>{ //定義預設陣列大小 private int defaultLenth=16; //負載因子,擴容標準 useSize/陣列長度>0.75擴容 private double defaultAddSizeFactor=0.75; //使用陣列位置的總數 private double useSize; //定義Map 骨架之一陣列 private Entry<K, V>[] table; public DIYHashMap(int defaultLenth, double defaultAddSizeFactor) { if(defaultLenth<0){ throw new IllegalArgumentException("陣列長度為負數"+defaultLenth); } if(defaultAddSizeFactor<=0 || Double.isNaN(defaultAddSizeFactor)){ throw new IllegalArgumentException("擴容標準必須大於0的數字"+defaultLenth); } this.defaultLenth = defaultLenth; this.defaultAddSizeFactor = defaultAddSizeFactor; table=new Entry[defaultLenth]; } //快速存取 hash演算法 public V put(K k, V v) { if(useSize>defaultAddSizeFactor*defaultLenth){ //擴容 up2Size(); } //通過key來計算出 儲存的位置 int index=getIndex(k,table.length); Entry<K, V> entry=table[index]; Entry<K, V> newEntry=new Entry<K, V>(k, v, null); if(entry==null){ table[index]=newEntry; useSize++; }else{//維護陣列相同位置佇列 Entry<K, V> tmp; while((tmp=table[index])!=null){ tmp=tmp.next; } tmp.next=newEntry; } return newEntry.getValue(); } private int getIndex(K k, int length) { //通常hashCode 取膜法 int m=length-1; int index=hash(k.hashCode()) & m; return index >= 0 ? index : -index; } //建立自己的hash演算法,保證計算出的位置 在陣列中均勻分佈 private int hash(int hashCode) { hashCode=hashCode^((hashCode>>>20)^(hashCode>>>12)); return hashCode^((hashCode>>>7)^(hashCode>>>4)); } //擴容陣列 private void up2Size() { Entry<K, V>[] newTable=new Entry[defaultLenth*2]; //將原table中的entry重新,雜湊到新的table中 againHash(newTable); } //將原table中的entry重新,雜湊到新的table中 private void againHash(Entry<K, V>[] newTable) { //數組裡面對象 封裝到list中,包括同一位置 有列表結構的都解析出來 List<Entry<K,V>> entryList=new ArrayList<Entry<K,V>>(); for(int i=0;i<table.length;i++){ if(table[i]==null){ continue; } findEntryByNext(table[i],entryList); } if(entryList.size()>0){ useSize=0; defaultLenth=defaultLenth*2; table=newTable; for (Entry<K, V> entry : entryList) { if(entry.next!=null){ entry.next=null; } put(entry.getKey(), entry.getValue()); } } } private void findEntryByNext(Entry<K, V> entry, List<Entry<K, V>> entryList) { if(entry!=null && entry.next!=null){ //這個entry物件已經形成連結串列結構 entryList.add(entry); //遞迴 將連結串列中的entry實體 都一次封裝到entryList連結串列中 findEntryByNext(entry.next, entryList); }else{ entryList.add(entry); } } //快取 public V get(K k) { //通過key來計算出 儲存的位置 int index=getIndex(k,table.length); Entry<K, V> entry=table[index]; if(entry==null){ throw new NullPointerException(); } return findValueByKey(k,entry); } private V findValueByKey(K k, Entry<K, V> entry) { if(k == entry.getKey() || k.equals(entry.getKey())){ return entry.v; }else if(entry.next!=null){ return findValueByKey(k,entry.next); } return null; } class Entry<K, V> implements DIYMap.Entry<K, V>{ K k; V v; //指向被this擠壓下去的entry Entry<K, V> next; public Entry(K k, V v, Entry<K, V> next) { this.k = k; this.v = v; this.next = next; } @Override public K getKey() { return k; } @Override public V getValue() { return v; } } }