- jdk1.7hashmap底层的数据结构是数组+链表;1.8之后是数组+链表+红黑树。
- 默认初始化容量为16,默认加载因子是0.75,代表当数组填满了75%的节点,就会触发数组扩容,每次扩容容量变为原容量的两倍。
- 当链表长度超过8;同时数组长度超过64,链表就会转变成红黑树,否则先扩容数组。当链表长度小于6,红黑树就变回链表。
- 一个类,如果其对象将会被放到hashmap中,那么该类应该重写hashcode和equals方法。因为hashmap是通过hashcode和equals来判断两个对象是否相同。首先对比两个对象的内存地址,一样就相等,否则接着对比hashcode的值,如果不一样就直接返回false,否则通过equals方法进行最后判断,相等就返回true,否则返回false。
- 在put元素的时候,会先对key进行hash,然后和数组容量-1进行按位与获得数组下标。如果没有hash冲突就创建节点将节点存入数组,如果有hash冲突就利用equals一个一个判断链表的节点key是否一样,一样就将value覆盖原理的value。如果一直到链表尾部都没有一样的就将节点接在链表尾部。然后判断是否需要转换成红黑树,如果链表长度超过8,并且数组长度达到64,就将链表转变成红黑树。
- 在使用的时候,可以通过预估大小设置默认容量来减少扩容次数来提高效率。设置的初始容量最好是2的倍数,即使不是2的倍数,构造函数内部也有操作将默认扩容设置为大于等于当前容量的最小的2的n次方值。为什么要这么设计?为了让hash表尽量散列。计算对象存放的数组下标是通过对象的hashcode和对象容量-1的结果(刚好是低位全1)进行按位与,这样就可以最大化散列。
- hashmap不是线程安全的,在多线程情况下,可能会出现不安全的情况。
扩容过程
1.7先创建一个原数组两倍容量的新数组,然后用一个临时引用指向原数组,将新数组赋给原数组引用。再然后遍历原数组链表上的每一个节点,重新计算hash值并与容量-1进行按位与获取新下标,然后利用头插法将节点一个个插入新数组。
1.8先创建一个原数组两倍容量的新数组,然后用一个临时引用指向原数组,将新数组赋给原数组引用。再然后遍历原数组上面的链表或者红黑树的每一个节点,重新计算hash值并与容量-1进行按位与获取新下标,然后将节点利用尾插法插入新数组,之后判断新数组上面的链表长度是否超过8,并且数组长度超过64,是就将数组转换成红黑树。