在JDK1.7及以前的版本中 ,HashMap 底层由 数组+ 链表 实现。
新建一个 HashMap的时候就会初始化一个数组。数组中的元素我们称为 Entry,每个 Entry其实就是一个 键值对,并且包含一个指向下一个元素的引用,这就构成了链表。当需要存储一个 键值对(Entry对象) 时,会根据hash算法来决定其在数组中的位置。当需要取出一个 Entry 对象时,也会根据 hash算法找到其在数组中的存储位置,再根据 equals 方法从该位置的链表中取出 Entry。
HashMap 结合了 ArrayList 的查询效率高的特点以及 LinkedList 插入效率高的特点,但是如果我们要存储的数据过于庞大,肯定会造成多次 哈希冲突,这样一来,链表上的节点就会堆积很多,在做查询的时候效率又会变得很低,失去了 HashMap本来的特点
在JDK1.8 及之后的版本中,HashMap 底层由 数组 + 链表 + 红黑树 实现。当节点数不大于 8 时,还是一个链表结构,只不过插入节点时变成了尾插法,当节点数大于 8 之后,将链表结构转换为红黑树结构,复杂度也从 O(n) 变成了 O(logn).
put方法在hashmap中起插入的作用,即在当前的hashmap上添加新元素。
主要流程:
- public v put(K key, V value) {
- if (key == null)
- return putForNullKey(value);
- // 通过 key 计算hash值
- int hash = hash(key);
- // 通过 key 找到数组中待存放的下标
- int i = indexFor(hash, table.length);
- // 链表
- for (Entry
e = table[i]; e != null; e = e.next) { - object k;
- // 如果这个数组下标中有元素,开始遍历链表
- if (e.hash == hash && ((k = e.key) == key || key .equals(k))) {
- // 如果两个 hash 值相同,或者键相同,则修改 value
- V oldValue = e.value;
- e.value = Value;
- e.recordAccess(this);
- return oldValue;
- }
- }
- modCount++;
- // 可能两个 hash 值不同,但计算出的 index 一样,这就发生了 hash碰撞
- // 使用头插法,将其放在链表头部
- addEntry (hash, key, value, i)
- return null;
- }
get方法在hashmap中起取值的作用,即根据 key 获取对应的 value值
主要流程:
- public V get(Object key) {
- Node
e; - return (e = getNode(hash(key), key)) == null ? null : e.value;
- }
-
- final Node
getNode(int hash, Object key) { - Node
[] tab; Node first, e; int n; K k; - //若哈希表不为空,并且当前索引位置有元素
- if ((tab = table) != null && (n = tab.length) > 0 &&
- (first = tab[(n - 1) & hash]) != null) {
- //若在一个桶中,并且当前索引位置的key值与要查找的key值相等
- //说明找到了,返回该值
- if (first.hash == hash &&
- ((k = first.key) == key || (key != null && key.equals(k))))
- return first;
- //若key值不相同,且还有元素
- if ((e = first.next) != null) {
- //如果此时已经树化,则调用红黑树的getTreeNode()查找
- if (first instanceof TreeNode)
- return ((TreeNode
)first).getTreeNode(hash, key); - do {
- //若没有树化,则在从前往后遍历链表,直到找到该元素
- if (e.hash == hash &&
- ((k = e.key) == key || (key != null && key.equals(k))))
- return e;
- } while ((e = e.next) != null);
- }
- }
- //若找不到则返回null
- return null;
- }
-