面试官:讲一下HashMap中的get方法,最好可以详细的讲一下getTreeNode方法的具体实现

前言

点赞在看,养成习惯。

点赞收藏,人生辉煌。

点击关注【微信搜索公众号:编程背锅侠】,防止迷路。

HashMap系列文章

第一篇 HashMap源码中的成员变量你还不懂? 来来来!!!整理好的成员变量源码解析

第二篇 撸啊撸,再次撸HashMap源码,踩坑源码中构造方法!!!每次都有收获

第三篇 MoxiMoxi !!!你看过HashMap中的put方法的源码吗?

第四篇 HashMap源码中的resize扩容方法除了扩容还有一个用途你真的知道吗?

第五篇 留一半清醒、留一半醉!!!HashMap中treeifyBin、treeify源码解析

第六篇 隔一段时间撸一次,特别香,HashMap中remove、getOrDefault源码,一遍一遍、又一遍

第六篇 面试官:讲一下HashMap中的get方法,最好可以详细的讲一下getTreeNode方法的具体实现

查找元素的方法

案例演示-普通查找
@Test
public void test_map_hash_get() {
 HashMap<Integer, String> map = new HashMap<>();
 map.put(1, "aaa");
 map.put(2, "bbb");
 map.put(3, "bbb111");   String s = map.get(2);  System.out.println(s); } 复制代码
案例演示-链表查找
@Test
public void test_map_link_get(){
 HashMap<String, Integer> map = new HashMap<>();
 for (int i = 0; i < 64; i++){
  map.put("BB" + i, i);
 }  System.out.println(map.size());  map.put("3Qj", 800);  map.put("2pj", 801);  map.put("2qK", 802);  map.put("2r,", 803);  map.put("3RK", 804);  map.put("3S,", 805);  map.put("42j", 806);  map.put("43K", 807);   Integer x = map.get("42j");  System.out.println(x); } 复制代码
案例演示-红黑树查找
@Test
public void test_map_link_tree(){
 HashMap<String, Integer> map = new HashMap<>();
 for (int i = 0; i < 64; i++){
  map.put("BB" + i, i);
 }  System.out.println(map.size());  map.put("3Qj", 800);  map.put("2pj", 801);  map.put("2qK", 802);  map.put("2r,", 803);  map.put("3RK", 804);  map.put("3S,", 805);  map.put("42j", 806);  map.put("43K", 807);  map.put("44,", 808);   Integer x = map.get("43K");  System.out.println(x); } 复制代码

源码分析

根据key查找对应的值
// 根据key查询对应的val
public V get(Object key) {
  // 定义一个node节点
 Node<K,V> e;
  // 根据key获取的节点为null返回这个null,否则获取的节点不为null,返回这个节点对应的值
 return (e = getNode(hash(key), key)) == null ? null : e.value; } 复制代码
根据哈希值和指定的key获取节点
// 根据指定的key和val获取节点
final Node<K,V> getNode(int hash, Object key) {
 Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
  // 如果哈希表不为空并且key对应的桶上不为空
 if ((tab = table) != null && (n = tab.length) > 0 &&
 (first = tab[(n - 1) & hash]) != null) {  // 判断数组元素是否相等  // 根据索引的位置检查第一个节点  // 注意:总是检查第一个节点  if (first.hash == hash && // always check first node  ((k = first.key) == key || (key != null && key.equals(k))))  // 如果是这个节点,返回这个节点  return first;  // 如果不是第一个节点,判断是否有后续节点  if ((e = first.next) != null) {  // 判断是否是红黑树,是的话调用红⿊树中的getTreeNode方法获取节点  if (first instanceof TreeNode)  return ((TreeNode<K,V>)first).getTreeNode(hash, key);  do {  // 不是红黑树的话,那就是链表结构了,通过循环的方法判断链表中是否存在该key  if (e.hash == hash &&  ((k = e.key) == key || (key != null && key.equals(k))))  // 返回这个节点  return e;  } while ((e = e.next) != null);  }  }  // 未获取到节点返回null  return null; } 复制代码
根据指定的哈希值和key获取树节点
// 参数h为哈希值,参数k为指定的key
final TreeNode<K,V> getTreeNode(int h, Object k) {
  // ((parent != null) ? root() : this)获取跟节点
  // 从跟节点开始查找指定的key
 return ((parent != null) ? root() : this).find(h, k, null);
} 复制代码
根据哈希值和指定的key查询节点
// h:哈希值。 k:给定的key
final TreeNode<K,V> find(int h, Object k, Class<?> kc) {
  // 首次遍历this是桶里面的第一个节点
 TreeNode<K,V> p = this;
  // 循环遍历这棵红黑树
 do {  int ph, dir; K pk;  // pl当前节点p的左孩子,pr当前节点p的右孩子  TreeNode<K,V> pl = p.left, pr = p.right, q;  // 将当前节点的哈希值赋值给ph,判断给定的哈希值是否小于当前节点的哈希值  if ((ph = p.hash) > h)  // 将左孩子赋值给p  p = pl;  // 当前节点的哈希值小于给定的哈希值  else if (ph < h)  // 将右孩子赋值给p  p = pr;  // 判断当前节点的key赋值给pk,并且pk与给定的k相等  else if ((pk = p.key) == k || (k != null && k.equals(pk)))  // 找到之后直接返回  return p;  // 判断左孩子是否null  else if (pl == null)  // 将右孩子赋值给p  p = pr;  // 判断右孩子pr是否为null  else if (pr == null)  // 将左孩子pl赋值给p  p = pl;  // 经过compare计算出dir  else if ((kc != null ||  (kc = comparableClassFor(k)) != null) &&  (dir = compareComparables(kc, k, pk)) != 0)  // 如果计算出的dir小于0,将pl左孩子赋值给p,否则将pr右孩子赋值给p   p = (dir < 0) ? pl : pr;  // 递归查找,查找到结果赋值给q  else if ((q = pr.find(h, k, kc)) != null)  // 返回q  return q;  else  // 将左孩子赋值给p  p = pl;  // 判断p是否为空,不为空接着循环  } while (p != null);  // 没有获取到节点直接返回  return null; } 复制代码
get方法的实现步骤
1、通过hash值获取该key映射到的桶
2、桶上的key就是要查找的key,则直接找到并返回
3、桶上的key不是要找的key,则查看后续的节点:
 - 如果后续节点是红黑树节点,通过调用红⿊树的方法根据key获取value
  - 如果后续节点是链表节点,则通过循环遍历链表根据key获取value
4、查找红黑树,由于之前添加时已经保证这个树是有序的了,因此查找时基本就是折半查找,效率更更高。  5、这⾥和插⼊时一样,如果对比节点的哈希值和要查找的哈希值相等,就会判断key是否相等,相等就直接返回。不相等就从⼦树中递归查找。  - 若为树,则在树中通过key.equals(k)查找,O(logn)  - 若为链表,则在链表中通过key.equals(k)查找,O(n)。 复制代码

谢谢点赞

  • 创作不易, 非常欢迎大家的点赞、评论和关注(^_−)☆
  • 你的点赞、评论以及关注是对我最大的支持和鼓励
  • 是我继续创作高质量博客的动力== !!!==

本文使用 mdnice 排版

猜你喜欢

转载自juejin.im/post/5f215fbd6fb9a07e911e9afe