HashMap (java.util.HashMap)是Map接口最基本的一个哈希表实现,它不是线程安全的,同时允许key为null。HashMap中的元素是无序的,也不保证顺序。HashMap的基本操作如get和put性能稳定,也就是在时间复杂度上是稳定的。同时,有两个影响HashMap性能的参数:初始容量和加载因子。
默认加载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f; |
默认容量
static final int DEFAULT_INITIAL_CAPACITY = 16; |
最大容量
HashMap最大容量为1 << 30,也就是2的30次方。
static final int MAXIMUM_CAPACITY = 1 << 30; |
The maximum capacity, used if a higher value is implicitly specified by either of the constructors with arguments. MUST be a power of two <= 1<<30. |
这个是HashMap中桶的最大容量,但并不是最大可put的元素的最大数量。最大可put的元素的数量为int的最大值,即2的31次方-1。
HashMap构造
public HashMap() |
Constructs an empty <tt>HashMap</tt> with the default initial capacity (16) and the default load factor (0.75). |
public HashMap(int initialCapacity) |
Constructs an empty <tt>HashMap</tt> with the specified initial capacity and the default load factor (0.75). |
public HashMap(int initialCapacity, float loadFactor) |
Constructs an empty <tt>HashMap</tt> with the specified initial capacity and load factor. |
public HashMap(Map<? extends K, ? extends V> m) |
Constructs a new <tt>HashMap</tt> with the same mappings as the specified <tt>Map</tt>. The <tt>HashMap</tt> is created with default load factor (0.75) and an initial capacity sufficient to hold the mappings in the specified <tt>Map</tt>. |
Threshold设置
threshold = (int)(capacity * loadFactor); |
计算落桶的位置
int i = indexFor(hash, table.length); |
static int indexFor(int h, int length) { return h & (length-1); } |
关键字比较
e.hash == hash && ((k = e.key) == key || key.equals(k)) |
扩容
当Map中的元素数目达到或超过threshold值时,将自动扩容,扩容后的容量是之前的两倍。
resize(2 * table.length) |
重新计算哈希
在进行扩容时,需要重新计算哈希。
关键代码
允许key为null
private V putForNullKey(V value) { for (Entry<K,V> e = table[0]; e != null; e = e.next) { if (e.key == null) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(0, null, value, 0); return null; } |
容量设置
默认情况下,如果没有指定初始容量,默认初始化容量为16。如果指定了初始容量,如采用public HashMap(int initialCapacity, float loadFactor)这种构造方式,这里第一个参数指定初始容量,但并不是简单的将HashMap容量设置为该参数值,而是像这样的:
// Find a power of 2 >= initialCapacity int capacity = 1; while (capacity < initialCapacity) capacity <<= 1; |
它实际上指定的是一个2的N次方的一个值,且这个值刚刚大于(不小于)指定的初始容量。从这里可以看出,HashMap的容量一定是一个2的N次方的一个值,且最大为1 << 30。
这段代码是什么意思?
if (oldCapacity == MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return; } |
在进行扩容时,如果当前容量已经达到最大容量,这将threshold值设置为为int的最大值,即2的31次方-1,并不再进行扩容,也就不需要再重新计算哈希。从这里可以看出:HashMap最大容量为1 << 30,也就是2的30次方;最大可put的元素的数量为int的最大值,即2的31次方-1。
HashMap实现的好吗?
HashMap实现的很好,但还不是很好。除了初始容量和加载因子这两个影响HashMap性能的参数,它还依赖于key的hashCode算法。同时,在计算哈希时计算了两次哈希。
static int hash(int h) { // 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); } |
这是第2次哈希计算,没具体研究。