其他分享
首页 > 其他分享> > 一篇带你吃透HashMap原理(附HashMap面试题夺命十三问)

一篇带你吃透HashMap原理(附HashMap面试题夺命十三问)

作者:互联网

4.6 HashMap

4.6.1 HashMap存储结构

这里用一张图来表示hashmap的存储结构。
在这里插入图片描述

4.6.2 HashMap底层原理

HashMap最底层时一个存储链表类型的数组对象。

transient Node<K,V>[] table;

当我们创建了HashMap对象,当我们需要放入元素时,链表数组table才被初始化。

Map<Integer,String> map = new HashMap<>();
map.put(1,"唐僧");

这个时候,底层用key的hash值和数组的长度-1进行与运算(位运算),计算出键值对应该存放在数组的下标。

hash(key);
if ((p = tab[i = (n - 1) & hash]) == null)

如果数组对应下标不存在值

将key,value,hash封装成链表结点Node,放入数组的指定位置中

tab[i] = newNode(hash, key, value, null);

这个时候,数组就放入了我们的第一个链表对象了。

preview

如果数组下标存在值,获取数组下标存在值(即链表)。

p = tab[i = (n - 1) & hash])

如果这个key是已经存在的,直接将value更换为传递进来的value。新建一个临时结点e。

if (p.hash == hash &&
    ((k = p.key) == key || (key != null && key.equals(k))))
    e = p;
V oldValue = e.value;

如果key不存在,判断链表是否是红黑树上的元素,若是,则在红黑树上进行增加元素操作。并指向临时结点e。

if (p instanceof TreeNode)
    e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);

若不是,则新建一个Node结点,并将新结点加入原链表后面。

e = p.next;
p.next = newNode(hash, key, value, null);
break;

如果结点数量等于8个,将链表转换为红黑树。

if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
    treeifyBin(tab, hash);

将e链表加入数组中后面

afterNodeAccess(e);

在最后,进行判断,如果元素个数达到12个,进行扩容resize

if (++size > threshold)
    resize();
afterNodeInsertion(evict);
return null;

4.6.3 一张图说清HashMap put原理

在这里插入图片描述

4.6.4resise方法

HashMap 的扩容实现机制是将老table数组中所有的Entry取出来,重新对其Hashcode做Hash散列到新的Table中

4.6.5 HashMap小知识

第一问: 为什么使用链表+数组:

第二问 我⽤LinkedList代替数组结构可以吗?

第三问 那既然可以使用进行替换处理,为什么有偏偏使用到数组呢?

第四问:那ArrayList,底层也是数组,查找也快啊,为啥不⽤ArrayList?

第五问:讲一讲HashMap的get/put过程。

put过程

  1. 根据key获取hash值,定位到数组下标,查看是否有值。
  2. 若没有值,直接封装一个含有hash,key,value的Node结点放入(table[index])中。
  3. 若有值,判断是否key的值是否和原有的值相等。若相等,替换原有value。
  4. 若不相等,判断结构是否为红黑树中结构,如果是红黑树结构,调用其他方法增加到红黑树的元素。
  5. 若不是红黑树结构,新建一个封装了数据的结点并加入到原链表的后面。判断元素个数是否达到8个
  6. 若达到,链表转化为红黑树。
  7. 判断总元素个数是否达到最大容量的0.75。若达到,resize扩容。
  8. 最终存储到数组结构中。

get过程

public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}
  1. 对key值的hash值进行位运算,获取下标值。
  2. 检查第一个结点,如果正确,直接返回。
  3. 如果是红黑树结构,使用红黑树的检索方法。O(logn)
  4. 如果是链表结构,遍历链表,找到key值对应的结点。O(n)
  5. 如果都没找到,返回空值。

第五问:还知道哪些hash算法

​ MD4,MD5加密算法。

第六问:知道jdk1.8中hashmap改了什么吗。

  1. 数组+链表结构改为数组+链表+红黑树
  2. 优化了高位的hash算法。
  3. 扩容后,元素要么在原来位置,或者原来位置的移动2次幂。

第七问:说一下为什么会出现线程的不安全性

第八问:为什么不一开始就使用红黑树,不是效率很高吗?

第九问:红黑树什么时候退化为链表

第十问:HashMap在并发环境下会有什么问题,一般是如何解决的。

第十一问:key可以是null吗,value可以是null吗

第十二问:一般用什么作为key值

​ 一般使用String作为key值

  1. 字符串是不可变的,它的hashcode值在创建时就被缓存,不需要重新创建。
  2. HashMap获取对象的时候需要使用正确的equals()和hashCode(),String正确的对它进行了重写。

第十三问:用可变类当Hashmap1的Key会有什么问题。

标签:面试题,hash,HashMap,数组,链表,夺命,value,key
来源: https://blog.csdn.net/super1223/article/details/117201308