HashMap底层实现原理解析
1、数组结构: 存储区间连续、内存占用严重、空间复杂度大
优点:随机读取和修改效率高,原因是数组是连续的(随机访问性强,查找速度快)
缺点:插入和删除数据效率低,因插入数据,这个位置后面的数据在内存中都要往后移动,且大小固定不易动态扩展。
2、链表结构:存储区间离散、占用内存宽松、空间复杂度小
优点:插入删除速度快,内存利用率高,没有固定大小,扩展灵活
缺点:不能随机查找,每次都是从第一个开始遍历(查询效率低)
3、哈希表结构:结合数组结构和链表结构的优点,从而实现了查询和修改效率高,插入和删除效率也高的一种数据结构
常见的HashMap就是这样的一种数据结构
HashMap中的put()和get()的实现原理:
1、map.put(k,v)实现原理
(1)首先将k,v封装到Node对象当中(节点)。
(2)然后它的底层会调用K的hashCode()方法得出hash值。
(3)通过哈希表函数/哈希算法,将hash值转换成数组的下标,下标位置上如果没有任何元素,就把Node添加到这个位置上。如果说下标对应的位置上有链表。此时,就会拿着k和链表上每个节点的k进行equal。如果所有的equals方法返回都是false,那么这个新的节点将被添加到链表的末尾。如其中有一个equals返回了true,那么这个节点的value将会被覆盖。
2、map.get(k)实现原理
(1)先调用k的hashCode()方法得出哈希值,并通过哈希算法转换成数组的下标。
(2)通过上一步哈希算法转换成数组的下标之后,在通过数组下标快速定位到某个位置上。如果这个位置上什么都没有,则返回null。如果这个位置上有单向链表,那么它就会拿着K和单向链表上的每一个节点的K进行equals,如果所有equals方法都返回false,则get方法返回null。如果其中一个节点的K和参数K进行equals返回true,那么此时该节点的value就是我们要找的value了,get方法最终返回这个要找的value。
相比 jdk1.7 的 HashMap 而言,jdk1.8最重要的就是引入了红黑树的设计,当hash表的单一链表长度超过 8 个的时候,链表结构就会转为红黑树结构。
HashMap常用参数
capacity: 当前数组的容量,默认为16。可以扩容,扩容后数组大小*2。
loadFactor: 负载因子,默认为0.75.
threshold: 扩容的阈值,= capacity * loadFactor。
ConcurrentHashMap
JDK 1.7版本之前,其有多个segment组成。每个segment均继承ReentrantLock并单独枷锁,所以每次进行加锁操作时锁住的都是一个segment。这样只要保证每个segment都是安全的,也就实现了全局的线程安全。其中有个参数叫conccurrencyLevel,默认值16,指16个segment,最多支持16个线程并发执行写操作,只要他们操作的数据分布在不同的segment上。conccurrencyLevel只能在初始化设置,一旦初始化后就不可以更改。
JDK 1.8之后,ConcurrentHashMap弃用了segment,改用Synchronized+CAS(CAS介绍)实现对多线程的安全操作。同时引入了红黑树结构。