HashMap实现原理(JDK版本:1.7.0_80)
Map<String, String> sMap = new HashMap<String, String>(); // 1.初始化 sMap.put("1", "子"); // 2.put操作 sMap.put("2", "丑"); sMap.put("3", "寅"); sMap.put("4", "卯"); String name = sMap.get("1"); // 3.get操作 for(Entry<String, String> entr : sMap.entrySet()){ System.out.println(entr.getKey() + ":" + entr.getValue()); }
1,初始化
类加载:
ClassLoader.loadClass(String name)
ClassLoader.checkPackageAccess(Class cls, ProtectionDomain pd)
HashMap初始化:
HashMap(); DEFAULT_INITIAL_CAPACITY=16 (容量) DEFAULT_LOAD_FACTOR=0.75 (负载因子)
2,put操作
HashMap的put操作:
public V put(K key, V value) { if (table == EMPTY_TABLE) { inflateTable(threshold); } if (key == null) return putForNullKey(value); int hash = hash(key); //1.根据key计算hashCode int i = indexFor(hash, table.length); //2.根据hashCode和哈希表大小计算下标 for (Entry<K,V> e = table[i]; e != null; e = e.next) { //3.根据下标和hashCode,key查询key是否已存在,若存在更新value Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); //4.key不存在,将新的键值对存入HashMap return null; }
HashCode计算:
final int hash(Object k) { //使用key计算hashCode int h = hashSeed; if (0 != h && k instanceof String) { return sun.misc.Hashing.stringHash32((String) k); } h ^= k.hashCode(); // This function ensures that hashCodes that differ only by // constant multiples at each bit position have a bounded // number of collisions (approximately 8 at default load factor). h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); }
位运算:
h 1111 1111 1111 1111 1111 0000 1110 1010
h >>> 16 0000 0000 0000 0000 1111 1111 1111 1111
h ^ (h >>> 16) 1111 1111 1111 1111 0000 1111 0001 0101
HashMap存储下标计算:
static int indexFor(int h, int length) { //HashCode和哈希表大小 // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2"; return h & (length-1); }
键值存储:
void addEntry(int hash, K key, V value, int bucketIndex) { if ((size >= threshold) && (null != table[bucketIndex])) { //threshold = 容量 * 负载因子 size = HashMap当前大小 resize(2 * table.length); //若当前HashMap大小>=threshold时,进行resize操作,大小为当前2倍 hash = (null != key) ? hash(key) : 0; bucketIndex = indexFor(hash, table.length); } createEntry(hash, key, value, bucketIndex); } void createEntry(int hash, K key, V value, int bucketIndex) { Entry<K,V> e = table[bucketIndex]; table[bucketIndex] = new Entry<>(hash, key, value, e); size++; }
Resize操作:
void resize(int newCapacity) { Entry[] oldTable = table; int oldCapacity = oldTable.length; if (oldCapacity == MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return; } Entry[] newTable = new Entry[newCapacity]; transfer(newTable, initHashSeedAsNeeded(newCapacity)); //将原来HashMap的内容移植到新HashMap中 table = newTable; threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1); }
void transfer(Entry[] newTable, boolean rehash) { int newCapacity = newTable.length; for (Entry<K,V> e : table) { while(null != e) { Entry<K,V> next = e.next; if (rehash) { //是否重新计算HashCode,只要resize该值应该为true e.hash = null == e.key ? 0 : hash(e.key); //1.重新根据key计算HashCode } int i = indexFor(e.hash, newCapacity); //2.根据HashCode和哈希表大小计算下标 e.next = newTable[i]; newTable[i] = e; //3.将原来键值的存储地址指向新哈希表 e = next; } } }
3,get操作
public V get(Object key) { if (key == null) return getForNullKey(); Entry<K,V> entry = getEntry(key); return null == entry ? null : entry.getValue(); }
final Entry<K,V> getEntry(Object key) { if (size == 0) { return null; } int hash = (key == null) ? 0 : hash(key); //1.根据key计算HashCode for (Entry<K,V> e = table[indexFor(hash, table.length)]; //2.根据下标和hashCode,key在HashMap中查询key对应的value e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } return null; }