Java集合—HashMap之hash优化算法

1. 原理

HashMap 底层是数组 + 链表 + 红黑树。

数组我们很熟悉,支持随机访问,所以在最优情况下,即 HashMap 没有出现 hash 冲突,没有形成链表或红黑树结构,此时数据都存在数组中,get 方法的查询时间复杂度为 O(1) 。

链表查询时间复杂度 O(n) ,红黑树 O(logn)。底层的数据结构和对应的时间复杂度是我们研究的基础。

再来看看 HashMap 继承或实现了哪些类和接口。

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {

如代码所示,HashMap 实现了 Cloneable、Serializable,说明支持克隆和序列化。

**备注:**所有关于 HashMap 的分析都是基于 JDK1.8 。

2. 源码解析

先来看看常用的基本属性。

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
static final float DEFAULT_LOAD_FACTOR = 0.75f;
int threshold;
static final int TREEIFY_THRESHOLD = 8;
static final int UNTREEIFY_THRESHOLD = 6;
transient Node<K,V>[] table;
transient int size;

默认 bucket 数组长度为16,默认负载因子 0.75 ,默认 threshold = 16 * 0.75 = 12 。当数组长度达到 12 时开始扩容。(这都不知道的同学建议去面壁思过)

TREEIFY_THRESHOLD 是指链表长度达到 8 以后转化为红黑树,UNTREEIFY_THRESHOLD 则相反。

size 是 HashMap 中元素的总个数,要和 table 的 length 区分开。

我们着重来看一下 Node ,因为数据都存储在 Node 中。

static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    Node<K,V> next;
  • hash: 根据存储元素的 key 的 hash 值
  • next:因为是数组,肯定要存储下一个节点

2.1 构造函数

思考一个问题:HashMap 是在什么时候给 bucket 数组分配内存的?是 new HashMap() 的时候吗?带着问题我们来看看HashMap 的四种构造函数。

2.1.1 HashMap()

public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}

可以看到,无参构造函数只是把 DEFAULT_LOAD_FACTOR 的值(默认 0.75)赋值给 loadFactor,loadFactor 是给 HashTable 用的,所以可以理解为啥正事都没干。

2.1.2 HashMap(int initialCapacity)

一般我们实际使用过程中都是用的这个,在新建一个 HashMap 的时候先预估存储的元素的个数,假设个数为 n ,然后用 n 除以 0.75 ,假设算出的值是 m ,initialCapacity 取不小于 m 的 2 的幂次方对应的值。

举个例子:我现假设要往 HashMap 中存入 17 个值,17 / 0.75 = 22.67 。取不小于 22.67 的最近的 2

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值