(1)数据结构:数组+链表。JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。Hashtable 没有这样的机制。
(2)线程安全:HashMap是非线程安全的。
(3)修改操作:
(3-1)对Null键和Null值的支持:HashMap可以存储Null的Key和Value,但Null作为键只能有一个,Null作为值可以有多个。
(3-2)初始容量大小和每次扩充容量大小:HashMap默认的初始化大小为16,之后每次扩充容量变为原来的2倍。创建时如果给定了容量初始值,HashMap会将其扩充为2的幂次方大小(HashMap中的tableSizeFor方法保证)。
(3-3)put操作的底层实现:HashMap使用key的hashCode经过扰动函数处理过后得到hash值,然后通过(n-1)&hash获取当前元素存放的位置(这里的n指的是数组的长度)。如果当前位置存在元素的话,就判断该元素与要存入的元素的hash值以及key 是否相同,如果相同的话直接覆盖,不相同就通过拉链法解决冲突。所谓扰动函数指的就是HashMap的hash方法,使用hash方法也就是扰动函数是为了防止一些出现比较差的hashCode方法,换句话说使用扰动函数之后可以减少碰撞。
(4)其他事项:
(4-1)HashMap的长度为什么是2的幂次方:我们一般采用%取余的操作来获取存放位置,但是取余操作中如果除数是2的幂次则等价于与其除数减一的与操作(也就是说hash%length==hash&(length-1)的前提是length是2的n次方)。采用二进制位操作&相对于%能够提高运算效率。
原文:https://www.cnblogs.com/StringBuilder/p/14762666.html