HashMap

HashMap

1. 哈希表的存储过程

  1. 根据key计算出哈希值h.
  2. 假设箱子个数为n,那么键值对应该放在第h%n个箱子中
  3. 如果箱子中有了键值对,应该解决冲突

2. 解决hash冲突

在使用链表解决冲突时,属于同一个箱子的所有键值对都会排列在链表中。
哈希表还有一个重要的属性: 负载因子(load factor),它用来衡量哈希表的 空/满 程度,一定程度上也可以体现查询的效率,计算公式为:

负载因子 = 总键值对数 / 箱子个数

负载因子越大,越容易导致冲突,性能也就越低。当负载因子大于某个常数(1,0.75)时,哈希表将自动扩容。理想状态下哈希表的每个箱子中,元素的数量遵循泊松分布:
泊松分布
当负载因子为 0.75 时,上述公式中 λ 约等于 0.5,因此箱子中元素个数和概率的关系如下:

数量 概率
0 0.60653066
1 0.30326533
2 0.07581633
3 0.01263606
4 0.00157952
5 0.00015795
6 0.00001316
7 0.00000094
8 0.00000006
所以,java8中,在一个箱子的数量大于8时,将链表转成红黑树,小于6时红黑树转成链表。一定程度上解决hash有问题导致一个箱子数量过大导致的而性能问题。

3. 扩容

HashMap,在元素个数大于 容量* 负载因子时,会进行扩容,默认情况下为16*0.75=12

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {

    .......
    // 这时候开始扩容
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

默认2倍扩容,并做rehash,就算hashcode 不做任何改变,rehash之后对应的箱子也是有可能不同的。对n取模后的值有变化。

posted @ 2018-04-10 15:51  dragonfei  阅读(191)  评论(0编辑  收藏  举报