HashMap的存储结构:
在JDK1.6,JDK1.7中,HashMap采用位桶(数组)+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,HashMap采用位桶(数组)+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。
HashMap的新建:
查看源码,当new一个HashMap时,会创建一个默认参数如下的对象,
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 初始容量为16
static final int MAXIMUM_CAPACITY = 1 << 30;//最大容量2的30次方
static final float DEFAULT_LOAD_FACTOR = 0.75f;//当链表数组的容量超过初始容量的0.75时,再散列将链表数组扩大2倍,把原链表数组的搬移到新的数组中
static final int TREEIFY_THRESHOLD = 8;//如果哈希函数不合理,即使扩容也无法减少箱子中链表的长度,因此 Java 的处理方案是当链表太长时,转换成红黑树。这个值表示当某个箱子中,链表长度大于 8 时,有可能会转化成树。
static final int UNTREEIFY_THRESHOLD = 6;//在哈希表扩容时,如果发现链表长度小于 6,则会由树重新退化为链表
static final int MIN_TREEIFY_CAPACITY = 64;//在转变成树之前,还会有一次判断,只有键值对数量大于 64 才会发生转换。这是为了避免在哈希表建立初期,多个键值对恰好被放入了同一个链表中而导致不必要的转化。
HashMap的put方法过程:
首先有一个每个元素都是Entry,当添加一个元素(key-value)时,就首先计算元素key的hash值,然后以hash值对位桶(数组)总数量取余,以此确定插入位桶(数组)的位置,但是可能存在同一hash值的元素已经被放在数组同一位置了,这时就添加到同一hash值的元素的后面,他们在数组的同一位置,但是形成了链表,同一各链表上的Hash值是相同的,所以说数组存放的是链表。而当链表长度太长时,链表就转换为红黑树(treemap),使用哈希值作为树的分支变量,如果两个哈希值不等,但指向同一个桶的话,较大的那个会插入到右子树里,如果哈希值相等,HashMap希望key值最好是实现了Comparable接口的,这样它可以按照顺序来进行插入。