Java HashMap的工作原理的工作原理
主要介绍了Java HashMap的工作原理的相关资料,需要的朋友可以参考下
大部分Java开发者都在使用Map,特别是HashMap。HashMap是一种简单但强大的方式去存储和获取数据。但有多少开发者
知道HashMap内部如何工作呢?几天前,我阅读了java.util.HashMap的大量源代码(包括Java 7 和Java 8),来深入理解这
个基础的数据结构。在这篇文章中,我会解释java.util.HashMap的实现,描述Java 8实现中添加的新特性,并讨论性能、内存
以及使用HashMap时的一些已知问题。
内部存储内部存储
Java HashMap类实现了Map<K, V>接口。这个接口中的主要方法包括:
V put(K key, V value)
V get(Object key)
V remove(Object key)
Boolean containsKey(Object key)
HashMap使用了一个内部类Entry<K, V>来存储数据。这个内部类是一个简单的键值对,并带有额外两个数据:
一个指向其他入口(译者注:引用对象)的引用,这样HashMap可以存储类似链接列表这样的对象。
一个用来代表键的哈希值,存储这个值可以避免HashMap在每次需要时都重新生成键所对应的哈希值。
下面是Entry<K, V>在Java 7下的一部分代码:
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
int hash;
…
}
HashMap将数据存储到多个单向Entry链表中(有时也被称为桶bucket或者容器orbins)。所有的列表都被注册到一个Entry数
组中(Entry<K, V>[]数组),这个内部数组的默认长度是16。
下面这幅图描述了一个HashMap实例的内部存储,它包含一个nullable对象组成的数组。每个对象都连接到另外一个对象,这
样就构成了一个链表。
所有具有相同哈希值的键都会被放到同一个链表(桶)中。具有不同哈希值的键最终可能会在相同的桶中。
当用户调用 put(K key, V value) 或者 get(Object key) 时,程序会计算对象应该在的桶的索引。然后,程序会迭代遍历对应的
列表,来寻找具有相同键的Entry对象(使用键的equals()方法)。
对于调用get()的情况,程序会返回值所对应的Entry对象(如果Entry对象存在)。
对于调用put(K key, V value)的情况,如果Entry对象已经存在,那么程序会将值替换为新值,否则,程序会在单向链表的表头
创建一个新的Entry(从参数中的键和值)。
桶(链表)的索引,是通过map的3个步骤生成的:
首先获取键的散列码。
程序重复散列码,来阻止针对键的糟糕的哈希函数,因为这有可能会将所有的数据都放到内部数组的相同的索引(桶)上。
程序拿到重复后的散列码,并对其使用数组长度(最小是1)的位掩码(bit-mask)。这个操作可以保证索引不会大于数组的
大小。你可以将其看做是一个经过计算的优化取模函数。
下面是生成索引的源代码:
// the "rehash" function in JAVA 7 that takes the hashcode of the key