HashMap总结
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HashMap总结相关的知识,希望对你有一定的参考价值。
1. 数据存储、类型:
数组(table):没有冲突的数据存放至数组中。
链表:冲突的数据形成一条单向链表,先put的在尾部后put的在头部,头部存至table中。
数据类型:java.util.HashMap.Entry<K, V> implements Map.Entry<K,V>,属性有:key、value、next、hash。
2. 加载因子、threshold、扩容:
加载因子:哈希表在其容量自动扩容之前可以达到多满的一个阈值,初始值为0.75。
threshold=容量 * 加载因子。
扩容:扩容后的table长度为原有的2倍,扩容条件如下:
if ((size >= threshold) && (null != table[bucketIndex])) { resize(2 * table.length); hash = (null != key) ? hash(key) : 0; bucketIndex = indexFor(hash, table.length); }
影响HashMap 性能的两个参数:初始容量和加载因子,尤其加载因子,如果加载因子过低就会增加table扩容次数并且数组利用率降低。如果过高则降低其查找性能(put、get)。
3. key的hash值的产生:
基于key的hashCode()方法产生数值,然后基于此数值计算得到最终的hash值;如果key为String类型则通过sun.misc.Hashing.stringHash32()方法直接产生最终hash值,源码如下:
final int hash(Object k) { 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); }
4. put:
根据key计算出hash值(当然了如果key为空特殊处理)。
根据hash值和table的长度计算此key应该存储至table的下标值index。
判断table的index元素是否为null:
如果为null,生成Entry存储至数组中下标为index的位置。
如果不为null并且hash值相等而且key也相同则用新的value替换老的value并返回老的value;如果key的hash值相等但是key不同则冲突发生,此时将形成一个链表,新来的作为头,之前的作为头的next。
相关源码如下:
public V put(K key, V value) { if (table == EMPTY_TABLE) { inflateTable(threshold); } if (key == null) return putForNullKey(value); int hash = hash(key); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { 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); return null; } static int indexFor(int h, int length) { // 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])) { resize(2 * table.length); 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++; }
5. get:
计算index的过程和put类似,然后判断table[index]是否为null如果是直接返回null,否则遍历以table[index]为头的链表,直到找到key的hash值相等并且key相同的Entry,然后返回其value值。相关源码:
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); for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } return null; }
6. key为null时只会存在table[0]开头的链表中。
以上是关于HashMap总结的主要内容,如果未能解决你的问题,请参考以下文章