HashMap-学习笔记

こ雲淡風輕ζ 提交于 2020-01-25 21:48:43
1.HashMap的特性
  • HashMap存储键值对(key : value) 实现快速存取,key跟value都允许为null,key只能有1个null
  • 非线程安全
  • 不能保证有序
2.HashMap的结构(jdk1.8)

HashMap采用数组 + 链表或红黑树,节点是用Node

3.HashMap的put方法执行过程
  1. 先对key执行hash方法运算 (注意当key为null的时候,hash方法会固定返回0,根据hash & (length -1)的算法判定存入的数据会保存在数组[0] 的位置)
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
  1. 如果散列列表为空的时候,即第一次执行put方法,会调用resize()生成一个长度为16的Node数组
   newCap = DEFAULT_INITIAL_CAPACITY;//数组默认的初始长度16
   newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);//扩容阈值 加载因子0.75*16
   
   Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
  1. 如果没有发生hash碰撞,直接将数据根据 hash & (length -1)的算法插入到数组对应的位置
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
  1. 如果发生hash碰撞,则进行3种判断
    1. 若key相同且equals后的内容相同,则替换旧值
    2. 若插入的是红黑树结构,则调用树的插入方法
    3. 链表结构,会循环遍历链表节点,内容相同则替换旧值;无相同内容在链表尾部进行插入,插入后会判定链表的长度是否达到变更红黑树的阈值8,如果达到则调用treeifyBin(tab, hash);方法进行转化。
  2. 如果元素个数超过扩容阈值的时候,会进行resize()扩容。
        if (++size > threshold)
            resize();
4.HashMap的resize()调用场景以及实现过程

调用场景:

  1. 初始化数组table
  2. 当元素个数超过扩容阈值的时候
  3. 当链表转化为红黑树,但是数组长度小于64的时候

实现过程:

  1. 通过判定数组数组长度是否为0
  2. 是:初始化数组table,默认长度为16,如果用户通过构造函数指定了一个数字作为容量,那么Hash会选择大于该数字的第一个2的幂作为容量。(3->4、7->8、9->16)
    否:进行扩容并扩容成两倍(在小于static final int MAXIMUM_CAPACITY = 1 << 30;的情况下),对元素重新进行排序,还是按照原来的hash & (length -1)算法,length为原来的两倍。在与运算下,hash与的位数不同将导致元素可能还在原来的位置上或者在原来位置*2的位置上 (原来长度为16,则跟hash值与运算的是1111,扩容后,长度为32,则跟hash值与运算的是11111)
关于HashMap的一些疑问?
  1. 为什么HashMap的长度要是2的n次方?
    减少hash碰撞==》https://blog.csdn.net/eaphyy/article/details/84386313
  2. 为什么链表长度达到8的时候转为红黑树,不是其他数字?
    https://blog.csdn.net/daiyuhe/article/details/89424736
  3. 为什么链表转为红黑树需要数组长度超过64?
    个人理解:避免恶意hash碰撞,导致数组长度过小的情况下出现红黑树影响性能。
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!