本文基于JDK1.8
1. 注释翻译
/**
* Returns the value to which the specified key is mapped,
* or {@code null} if this map contains no mapping for the key.
* 返回指定的key映射的值value,如果此映射不包含key键的一射,则返回null。
* <p>More formally, if this map contains a mapping from a key
* {@code k} to a value {@code v} such that {@code (key==null ? k==null :
* key.equals(k))}, then this method returns {@code v}; otherwise
* it returns {@code null}. (There can be at most one such mapping.)
* 更正式的说,如果此映射包含从键key到值value的映射,那么执行(key==null ? k==null :
* key.equals(k))操作,然后此方法返回返回值value,否则返回null。(最多可以有一个这样的映射。)
* <p>A return value of {@code null} does not <i>necessarily</i>
* indicate that the map contains no mapping for the key; it's also
* possible that the map explicitly maps the key to {@code null}.
* The {@link #containsKey containsKey} operation may be used to
* distinguish these two cases.
* 返回null不代表地图中不包含该key键的映射;有可能这个地图中,将key键映射的值value为null。containsKey操作可用于区分这两种情况。
*
* @see #put(Object, Object)
*/
从上面的注释我们大概的了解到了,get()方法的作用查找哈希表中对应key键的value值,如果所对应的key键在map中有映射的value值,则返回value值,否则返回null。
当get()方法返回为null时,有一个特殊的情况,那就是key映射到map中的value值本身就为空。在本方法中无法区分它们,但是在containsKey方法中可以区分它们。
2. 源码剖析
public V get(Object key) {
Node<K,V> e;
// 在这里执行getNode操作,要了解这里的具体操作我们需要先看一下getNode方法,我们在下面分析一下这个方法。
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
getNode方法
/**
* Implements Map.get and related methods
* 实现Map.get和相关方法
* @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) {
// 要查找结点的key值等于桶中头节点的key值,直接返回头节点
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
// 判断桶中是否有其他结点,进行痛的遍历
if ((e = first.next) != null) {
// 若桶中元素已经树化,使用树的方式查找结点
if (first instanceof TreeNode)
return ((TreeNode<K,V>)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);
}
}
// 哈希表为空,或桶为空
return null;
}
我们通过源码分析可以发现,get()方法的核心是调用了getNode()方法,getNode()方法,返回了key键所在的结点,如果没有则返回null,get()方法对该值进行判断,然后取出value进行操作。
3. get()执行流程
- 若哈希表已经初始化并且桶的首结点不为空
① 查找结点的key值恰好等于首结点的key值,直接返回首结点。
② 进行桶中元素的遍历,查找指定结点
a. 若树化,按照树的方法查找指定结点
b. 按照链表方式查找 - 哈希表为空或桶的首结点为null,直接返回null。
4. containsKey(Object key)
/**
* Returns <tt>true</tt> if this map contains a mapping for the
* specified key.
* 如果此映射包含指定key键的映射,则返回true
*
* @param key The key whose presence in this map is to be tested
* @return <tt>true</tt> if this map contains a mapping for the specified
* key.
*/
public boolean containsKey(Object key) {
return getNode(hash(key), key) != null;
}
该方法也是调用了getNode方法,在这里方法的作用时,判断该key值在map中是否含有映射关系,如果含有映射关系,则返回true,狗则返回false。