HashMap随笔

Posted sea-and-fish

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HashMap随笔相关的知识,希望对你有一定的参考价值。

1:HashMap 的数据结构?

  答:HashMap底层是数组 + 链表 + 红黑树的数据结构,数组的主要作用是方便快速查找,时间复杂读是O(1),

    默认大小是16,数组的下标的通过key 的hashcode 计算出来的,数组元素叫做Node节点,当多个 key的 

    hashcode 一致,但 key 值不同时,单个Node节点就会转化成链表,链表的查询复杂度O(n), 当链表的

    长度大于等于8 并且数组的大小超过64时,链表就会转化成红黑树,红黑树的查询复杂度是O(log(n)),

    所以,红黑树的最大深度就等于链表的最坏查询次数。

      transient Node<K,V>[] table;

2:HashMap 的工作原理?

    答:HashMap 底层是hash数组和单项链表实现,数组中的每个元素都是链表,由 Node<K,V> 实现Map.Entry<K,V>接口实现,

      HashMap 通过put & get 方法存储和获取。

     存储对象时,将K/V键值传给 put():

      1、调用hash 函数获取key对应的hash值,再计算数组下标;

      2、如果没有出现hash 冲突,则直接放入数组;如果出现hash 冲突,则以链表的方式放在链表后面;

      3、如果链表长度超过(阈值 8),则把链表转换成红黑树,链表长度低于6,就把红黑树转会链表;

      4.如果节点的key 已存在,则替换其value值;

      5.如果Map中键值对大于12,则调用resize 方法进行扩容;

     获取对象时,通过get 方法实现:

      1、判断表或key是否是null,如果是直接返回null;

      2、判断索引处第一个key与传入key是否相等,如果相等直接返回;

      3、如果不相等,判断链表是否是红黑二叉树,如果是,直接从树中取值;

      4、如果不是树,就遍历链表查找;

      注:多线程环境下,HashMap 写操作的时候是线程不安全的,而读操作的时候是线程安全的;

3:为了解决Hash 冲突,大概有哪些方法?

      答:1、好的 hash 算法;

        2、自动扩容,但数组大小快满的时候,采取自动扩容,可以减少 hash 冲突;

        3、hash 冲突发生时,采用链表来解决;

        4、hash 冲突严重时,链表会自动转化为红黑树,提高遍历速度;

4:HashMap 如何进行扩容?

    答:发生扩容的时机:

        1、put 时,发现数组为空,进行初始化扩容,默认扩容大小为16;

        2、put 成功后,发现现有数组大小大于扩容的阈值时,进行扩容,扩容为老数组大小的2倍;

        扩容的阈值是 threshold, 每次扩容时 threshold 都会被重新计算,阈值等于数组大小 * 影响因子(0.75),

         新数组初始化之后,需要将老数组的值拷贝到新数组上;

5:jdk8中对HashMap做了哪些改变?

   答:1、在Java1.8中,Entry被Node替代;

     2、发生hash 碰撞时,Java 1.7 会在链表的头部插入,而 Java 1.8会在链表的尾部插入;

     3、在java 1.8中,如果链表的长度超过了8,那么链表将转换为红黑树。(桶的数量必须大于64,小于64的时候只会扩容);

6:为什么HashMap不是线程安全的?

    答:Java 1.7在多线程操作 put 方法可能引起死循环,原因是扩容转移后前后链表顺序倒置(1.7版本的链表是从头插入的,而1.8变为从尾部插入),在转移过程中修改了原来链表中节点的引用关系;

      Java 1.8在同样的前提下并不会引起死循环,原因是扩容转移后前后链表顺序不变,保持之前节点的引用关系;

以上是关于HashMap随笔的主要内容,如果未能解决你的问题,请参考以下文章

HashMap一问一答

浅谈HashMap

8,HashMap子类-LinkedHashMap

HashMap集合与ArrayList集合的遍历

Java并发容器--ConcurrentHashMap

数据结构随笔