HashMap的get,containKey, containsValue方法

前面我们基本把put方法的过程解析完了,这一篇我们看一下与查询有关的方法。

get 方法

下面我们先看一下通过key获取value的方法get。

public V get(Object key) {Node<K,V> e;return (e = getNode(hash(key), key)) == null ? null : e.value;
}

这里可以看到,其重要的就是如何获取传入的key所对应的节点,一旦我们获取到了节点,就可以拿到值了,我们看一下getNode方法

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) {if (first.hash == hash && ((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;
}

上面代码就是获取节点的方法,我们记下来一步一步看。

if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) 

首先要保证数组已经创建出来,不能实例化HashMap就从中get数据,肯定是null。并且此时的table的长度要大于0. 然后通过hash和容量的与运算计算节点的桶位(如何计算的,请看上一节put)。此时要保证计算到的位置上的节点不为null,否则就是不存在。

当上述条件成立之后,我们看是分为三种情况获取:

  • ① 数组上的节点就是要查询的节点
  • ② 需要遍历链表看查询节点
  • ③ 需要查询红黑树

第一种:数组上

if (first.hash == hash && ((k = first.key) == key || (key != null && key.equals(k))))

这个判断我们在put的时候也见过。需要保证传入的hash与节点的hash值一致,同时保证key是相同的。

第二种:红黑树

return ((TreeNode<K,V>)first).getTreeNode(hash, key);

红黑树的查找不做深究,但是我们进去看一下,也基本可以看懂,其实就是通过hash值的判断来选取查询左子树或是右子树。

第三种:链表

do {//链表if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))return e;
} while ((e = e.next) != null);

链表的查询其实基本和第一种一样,只要掌握了链表的结构特点,其实是比较简单的。

到这里,get方法其实就分析完了

containsKey 方法

public boolean containsKey(Object key) {return getNode(hash(key), key) != null;
}

我们看完上面的get方法之后,明白了getNode方法,再看containsKey就比较容易了。其实就是看getNode查询到的节点是不是null,就可以判断这个key是不是存在了。

containsValue 方法

public boolean containsValue(Object value) {Node<K,V>[] tab; V v;if ((tab = table) != null && size > 0) {for (int i = 0; i < tab.length; ++i) {//可以看到,这里是使用链表的方式来查询是否包含这个value。for (Node<K,V> e = tab[i]; e != null; e = e.next) {if ((v = e.value) == value || (value != null && value.equals(v)))return true;}}}return false;
}

查询指定值是否存在就和判断key是否存在不同了,因为key是可以通过计算hash值来查询的,这样就可以将查询效率进行优化,但是查询值的话只能通过一个一个遍历的方式来查询。

我们通过前面的put方法可以知道,虽然当链表长度超过8的时候,会将链表转为红黑树,但是其红黑树结构依旧维护了双向链表的结构,所以当我们无法通过key去查找的时候,我们可以将其统一当前链表来查询【这种方式效率低于正常key查询】。

这样,我们再回头看上面的代码就可以知道了,外层循环遍历table数组,内层循环遍历链表数据,一旦查找到相同的value就返回true,否则就返回false。

ooyhao
原创文章 180获赞 62访问量 17万+
关注私信
展开阅读全文