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随笔的主要内容,如果未能解决你的问题,请参考以下文章