1、HashMap的工作原理
1.1、put
- 当使用put(key, value)存储对象到HashMap中时,会首先对键调用hashCode()方法,计算并返回hashCode是用于找到Map数组的bucket位置来存储Node对象。
- 如果Hash值对应的数组元素没有发生碰撞(无值),则直接放入当前位置中;若发生了碰撞,则以链表的方式链接到后面。
- 若链表长度超过阈值(一般为8),则将链表转化为红黑树,链表长度若低于一定值(6),就把红黑树转化为链表。
- 若链表或红黑树中已经存在,则替代它。
- 若桶满了(容量16*加载因子0.75),就需要resize(扩容2倍后重新排列)。
1.2、get
- 当我们调用get()方法时,HashMap会通过键对象hashcode找到对应的数组位置,找到之后通过keys.equals()方法找到链表中正确的节点,最终找到正确的对象。
1.3、减少碰撞的方法?
- 为什么String适合做为键:因为String是final的,而且重写了equals()和hashCode()方法。如果要计算hashCode就要防止键值改变,如果键值在放入时和获取时返回不同的hashCode的话,就不能哦才能够HashMap中找到想要的对象。
1.4、hash过程?
- hash公式:hashCode(key) & (Length - 1);
1.5、为什么使用红黑树?为什么不一直使用红黑树?
- 二叉树在特殊情况下会变成一条线性结构,遍历跟之前一样。红黑树在插入新数据之后回经过左旋、右旋、变色这些操作来保持平衡。
- 红黑树属于平衡二叉树,如果链表端的话,会损耗性能;链表长的时候,红黑树效率更高。
1.6、红黑树的理解?
见3。
1.7、若HashMap大小超过了负载因子定义的容量怎么办?
- 当一个Map填满了75%的bucket时,会将原来HashMao大小的两倍的bucket数组来调整Map的大小,将原来的对象放入新的bucket数组中。
- 这个值可能在两个地方,一个是原下标的位置,另一个实在下标为<原下标+原容量>的位置。
1.8、重新调整的HashMap存在什么问题?
- 重新调整HashMap大小的时候,存在条件竞争。
- 如果两个线程同时发现了HashMap需要调整大小,会同时试着调整大小。在调整大小的过程中,链表会首位互换,这是为了避免尾部遍历,若竞争发生,就是死循环。
1.9、HashMap的扩容?
- 认识两个变量Capacity:HashMap的当前长度,是2的幂;LoadFactor:HashMap的负载因子,默认值为0.75f。衡量是否需要进行Resize的条件是:HashMap.Size >= Capacity * LoadFactor。
- Resize的步骤:
1)、扩容:创建一个新的Entry空数组,长度是原数组的2倍。
2)、ReHash:遍历原Entry数组,把所有的Entry重新Hash到新的数组。 - 可能会出现死链:两个线程导致链表出现环形,导致了死循环。
2、ConcurrentHashMap
2.1、可以使用ConcurrentHashMap代替HashTable么?
- ConcurrentHashMap的同步性能比HashTable更好,因为仅仅根据同步级别对map的一部分上锁;
- ConcurrentHashMap可以代替HashTable,但是HashTable可以提供更强的线程安全;
- 他们都可以用到多线程环境。当HashTable的大小增大到一定量的时候,性能会下降,因为迭代需要锁定的时间会很长。ConcurrentHashMap引入了分割,无论Map变得多大,仅需要锁定Map的某个部分,其他线程不需要等到迭代完成才能访问Map。ConcurrentHashMap会锁定Map的某个部分,HashTable会锁定整个Map。
2.2、ConcurrentHashMap运行原理
- 采用了CAS+synchronized来保证并发安全性。
2.2.1、CAS
- CAS有三个操作数:V内存值、A旧的预期值、B要修改的新值。当且仅当A和V相同时,将内存值V修改为B,否则什么都不做。
- CAS会出现的问题:ABA;
解决方案:对变量增加一个版本号,每次修改版本号加1,比较的时候比较版本号。
2.2.2、put过程
- 根据key计算出hashcode;
- 判断是否需要进行初始化(即Map为空),若需要,则初始化;
- 判断该节点上是否存在数据,若不存在,则直接赋值;若存在,则若该节点的hash值为MOVED(-1),则对该桶中的节点进行转移;
- 若hash值不是-1,则对bucket中的第一个节点(即数组元素)进行加锁(synchronized),对该bucket进行遍历,bucket中的hash值与key和给定的hash值与key相等,则替换value值;若没找到相等的,则直接新生成一个结点并赋值为最后一个节点的下一个节点;
- 若bitCount值达到红黑树转化的阈值,则将bucket中的结构转化为红黑树。
3、红黑树
3.1、简介
-
每个节点是红色或者黑色;
根节点是黑色;
每个叶子节点是黑色(指为空的叶子节点);
若一个节点是红色,则他的子节点必须是黑色;
从一个节点到该节点的子孙节点所有路径上包含相同数目的黑节点;
新插入节点为红色节点;
任意节点左右子树最多相差两层红色节点; -
时间复杂度为O(logn)
-
左旋、右旋
-
添加的步骤:将红黑树当作一棵二叉查找树,将节点插入;将插入节点着色为“红色”(为了上述第五点);通过旋转和着色操作,使其成为一棵红黑树。
-
红黑树性能要比平衡二叉树高