HashMap特性
HashMap存储的是键值对,允许为null,key值不可重复,如果重复,value将被覆盖。
非同步,线程不安全,无序
底层原理
jdk8后采用的是:数组+链表+红黑树
当使用put方法时,会先对key做一个hashcode值计算,找到在bucket数组中的位置,来存储Entry对象;如果hashcode值相同就通过equals方法进行比较;如果equals方法返回false,就将数据以链表的形式存储在数组对应的位置,并将之前在该位置的数据往链表的后面移动(头插法),并记录一个next属性,来指示后移的那个数据。
当使用get方法时,同上
头插法
HashMap会将Entry对象不断插入链表的头部,这样也防止来尾遍历,否则Entry每次添加还要定位到尾节点。如果条件竞争(多线程操作),可能会出现环形链表,当使用get操作时,有可能发生死循环。
put方法如何实现
- 计算key的hashcode值
- 如果散列表为空时,调用resize()方法初始化散列表
- 如果没有发生碰撞,直接将元素添加到散列表中
- 如果发生了hash碰撞,进行判断,如果key值相同,替换value值;如果是链表结构,在链表的尾部进行插入,如果链表个数达到红黑树的阀值8,转红黑树结构;如果是红黑树结构,调用树的插入方法。
- 如果元素总数大于阀值,就会调用resize()方法进行扩容
扩容实现resize()方法
默认负载因子大小为0.75,当一个map填满75%的bucket时,将会创建原HashMap2倍大小的bucket,并将原来的对象重新计算放入新的bucket中。
哈希碰撞
key值不同,但是通过hashCode计算的值却一样。发生碰撞后,HashMap使用LinkedList存储对象(键值对)
为什么引入红黑树
jdk1.8以前,数据结构是数组+链表
当hashMap中有大量的数据都存放在一个桶中时,这个桶就会有一个很长的链表,时间遍历复杂度是O(n),针对这种问题,引入了红黑树来优化。
为什么String,Integer这样的Wrapper类适合作为主键
String是不可变类,String/Integer都重写了hashCode()和equals()方法。如果使用对象的话,就需要重写其hashCode()和equals()方法。并且这个对象是不可变的。