回顾一下上一篇文章,介绍的是put 相关函数。了解了HashMap put的过程,什么时候扩容以及什么时候将链表转为红黑树。接下来将介绍HashMap get相关函数以及常见的面试题。
get 相关函数
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
/**
* Implements Map.get and related methods
*
* @param hash hash for key
* @param key the key
* @return the node, or null if none
*/
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {//注释1
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))//注释2
return first;
if ((e = first.next) != null) {
if (first instanceof TreeNode)//注释3
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {//注释4
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
可以看到get(Object key) 函数的主要逻辑是在getNode(int hash, Object key)中,相对而言逻辑是比较简单的。
- 注释1:判空处理,判断tab 是否为空,tab[(n - 1) & hash]) 是否为空
- 注释2:判断first 是否为我们要找的node,是就直接返回,否则继续往下
- 注释3:判断first 是否为红黑树,是就调用getTreeNode(hash, key),否则继续
- 注释4:遍历链表查找我们需要的node 找到就返回
- 以上都为找到就返回null
常见的面试题
-
HashMap 的数据结构?
哈希表结构(链表散列:数组+链表)实现,结合数组和链表的优点。当链表长度超过 8 时,链表转换为红黑树。 -
HashMap的底层原理是什么?
基于hashing的原理,jdk8后采用数组+链表+红黑树的数据结构。我们通过put和get存储和获取对象。当我们给put()方法传递键和值时,先对键做一个hashCode()的计算来得到它在bucket数组中的位置来存储Entry对象。当获取对象时,通过get获取到bucket的位置,再通过键对象的equals()方法找到正确的键值对,然后在返回值对象。 -
HashMap 的底层数组长度为何总是2的n次方
HashMap根据用户传入的初始化容量,利用无符号右移和按位或运算等方式计算出第一个大于该数的2的幂。使数据分布均匀,减少碰撞当length为2的n次方时,h&(length - 1) 就相当于对length取模,而且在速度、效率上比直接取模要快得多。 -
hash 碰撞的处理 (以下问题留给读者)
-
HashMap 扩容机制
-
HashMap和Hashtable的区别