HashMap在jdk7.0中的底层实现原理:
实例化对象
HashMap map = new HashMap();
- 在实例化之后,底层创建了一个长度为16的一维数组table(这个数组是Entry类型的);
put();
map.put(key1,value1);
- key1和value1是以一个整体的Entry对象的形式传递进HashMap底层的table数组中
- Map中没有add()方法,Map中添加数据我们是通过使用put()方法进行添加
首先,我们先调用key1所在类的hashCode()方法,计算出key1的哈希值,此哈希值经过某种算法之后(散列函数: 其实就是给我们计算出来的hash值取模16,但是这个时候我们取模16在底层中我们是通过&(与运算)15来实现的, 这个时候通过&15来代替 %16,就是因为我们的&15在底层执行的效率更加高一些),就可以得到我们的这个Entry对象在Entry数组中的存放位置
- 如果这个位置上没有其他数据,这个时候我们就说我们的这个key1对应的Entry对象添加成功 ----- 情况一
- 如果此位置上已经有了一个或者以链表形式存储的多个Entry对象时,这个时候我们就要通过我们的key1的hash值和原来位置上这里已经存在的Entry对象的key的hash值进行比较
- 如果这个时候我们key1的hash值和我们这里原本的这些Entry对象的key的hash值都不相同,这个时候我们的key1对应的Entry对象也添加成功 ------ 情况二
- 如果这个时候我们的key1的hash值和我们这里原本这里存在的Entry对象中的某个的key的hash值相同,这个时候我们使用key1所在类的equals()方法和与key1hash值相同的key进行比较
- 如果这个时候equals()方法的返回值为false,这个时候我们的key1对应的Entry对象也添加成功 ----情况三
- 如果这个时候equals()方法的返回值为true,这个时候用我们的value1替换这个与我们的key1所重复的Entry对象中的value
- 对于这里的情况二和情况三,这个时候我们新添加的key ---- value键值对和原本这个位置上的Entry对象以链表的形式进行存储
- 这里既然要以链表的形式存储,这个时候就要设计谁链谁:
- jdk7中,我们新添加的数据在数组中,指向原有数据
- jdk8中,原有数据在数组中,指向新添加的数据
- 总结:“七上八下”
关于这里使用put()方法的总结:
我们之前使用的List和Set中的add()方法都是返回一个boolean类型的数据
- 也就是使用add()方法分为添加成功和添加失败
- 其实也就是add()方法只有添加元素的功能
我们在Map中使用的put()方法返回的是一个V型的数据,这个V是一个泛型(是value对应的泛型)
- 我们Map中的put()方法有添加元素的功能还有修改元素的功能
- 我们使用put()方法如果是要添加一个元素,如果添加成功了,那么就是添加成功了,如果添加失败了,这个时候其实不是添加失败了,这个时候是对这个原有的重复元素进行了修改
扩容问题(后面细讲)
在HashMap集合中添加对象时,在不断添加的过程中,我们会涉及到数组扩容问题,这里默认的扩容方式为扩容为原来的2倍 ,并且我们扩容之后数组中药进行元素的重排,也就是这个时候我们底层的数组变化了,对应不同的散列函数我们要重新以一个新的顺序进行存储
jdk7.0中通过单独方法进行初始化
inflateTable();
jdk7.0中的扩容的方法
resize();
- 在jdk8.0中我们将初始化也加到了resize()方法中来,也就是我们在jdk8.0中通过使用resize()方法完成数组的初始化和扩容