1. HashMap结构
简单来说,HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表(当前entry的next指向null),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度为O(n),首先遍历链表,存在即覆盖,否则新增;对于查找操作来讲,仍需遍历链表,然后通过key对象的equals方法逐一比对查找。所以,性能考虑,HashMap中的链表出现越少,性能才会越好。
2. 如何定位数组下标
对key的hashcode,进行hash()运算,得出h值,然后与hashMap的length-1做与运算,计算出数组下标。
h&(length-1)保证获取的index一定在数组范围内,举个例子,默认容量16,length-1=15,h=18,转换成二进制计算为
1 0 0 1 0 & 0 1 1 1 1 __________________ 0 0 0 1 0 = 2
计算出数组下标为2.
3. 扩容机制
当HashMap的大小超过阈值时,触发扩容机制。
扩容时,需要新建一个长度为之前数组2倍的新的数组,然后将当前的Entry数组中的元素全部传输过去,扩容后的新数组长度为之前的2倍,所以扩容相对来说是个耗资源的操作。
扩容时,遍历所有的entry,根据新的length,定位数组下标,将数据放入新HashMap中对应下标下的链表中。
4. get方法
key(hashcode)-->hash-->indexFor-->最终索引位置,找到对应位置table[i],再查看是否有链表,遍历链表,通过key的equals方法比对查找对应的记录。
5. 重写equals方法要同时重写HashCode方法
如果没有重写hashCode方法,put操作时,key(hashcode1)-->hash-->indexFor-->最终索引位置 ,而通过keygeti操作的时候 key(hashcode2)-->hash-->indexFor-->最终索引位置,由于hashcode1不等于hashcode2,导致没有定位到一个数组位置而返回逻辑上错误的值null