HashMap源码剖析

首先看看HashMap中一些属性:
//默认的初始容量(容量为HashMap中槽的数目)是16,且实际容量必须是2的整数次幂。
static final int DEFAULT_INITIAL_CAPACITY = 16;
// 最大容量(必须是2的幂且小于2的30次方,传入容量过大将被这个值替换)
static final int MAXIMUM_CAPACITY = 1 << 30;
// 默认加载因子为0.75
static final float DEFAULT_LOAD_FACTOR = 0.75f;
// 存储数据的Entry数组,长度是2的幂。HashMap采用链表法解决冲突,每一个Entry本质上是一个单向链表。
transient Entry[] table;
// HashMap的底层数组中已用槽的数量
transient int size;
// HashMap的阈值,用于判断是否需要调整HashMap的容量(threshold = 容量*加载因子)
int threshold;
// 加载因子实际大小
final float loadFactor;
// HashMap被改变的次数
transient volatile int modCount;

//根据key的hashcode重新计算hash值

static int hash(int h) {    
    h ^= (h >>> 20) ^ (h >>> 12);    
    return h ^ (h >>> 7) ^ (h >>> 4);    
}   

//返回h在数组中的索引值。这里用&代替取模,旨在提升效率。 h & (length-1)保证返回值的小于length。
static int indexFor(int h, int length) {
return h & (length-1);
}
重点说一下由hash值找到对应索引的方法indexFor():
一般我们对哈希表的散列是采用“除法散列法”,Hashtable中是这样实现的。
但取模用到除法运算,效率低下,所以HashMap通过h & (length-1)代替取模同样实现均匀的散列。
HashMap采用h & (length-1)这种方式进行哈希散列的前提是因为规定哈希表的容量必须是2的整数次幂。
原因如下:
①length为2的整数次幂的话,h&(length-1)就相当于对length取模,这样便保证了散列的均匀,同时也提升了效率;
②其次,length为2的整数次幂的话,为偶数,这样length-1为奇数,奇数的最后一位是1,这样便保证了h&(length-1)的最后一位可能为0,也可能为1(这取决于h的值),即与后的结果可能为偶数,也可能为奇数,这样便可以保证散列的均匀性,而如果length为奇数的话,很明显length-1为偶数,它的最后一位是0,这样h&(length-1)的最后一位肯定为0,即只能为偶数,这样任何hash值都只会被散列到数组的偶数下标位置上,这便浪费了近一半的空间
综上所述:length取2的整数次幂,是为了使不同hash值发生碰撞的概率较小,这样就能使元素在哈希表中均匀地散列。

扩容。newCapacity是调整后的单位。
void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) { //如果容量已经达到HashMap最大值。
threshold = Integer.MAX_VALUE; //则将Integer.MAX_VALUE:2**31-1赋给阀值。
return;
}
Entry[] newTable = new Entry[newCapacity];
transfer(newTable); //调用transfer方法,将就HashMap的全部元素添加到新的HashMap中。
table = newTable; //将新HashMap赋值给旧HashMap。
threshold = (int)(newCapacity * loadFactor); //最后再调整HashMap的阀值。
}
void transfer(Entry[] newTable) {
Entry[] src = table;
int newCapacity = newTable.length;
//迭代老数组中的元素,根据扩容后的新长度,调用indexFor()重新计算新的索引位置。
for (int j = 0; j < src.length; j++) {
Entry

    // 是否包含null值 (同样是对数组的每个链表遍历寻找)
    private boolean containsNullValue() {    
    Entry[] tab = table;    
    for (int i = 0; i < tab.length ; i++)    
       for (Entry e = tab[i] ; e != null ; e = e.next)    
            if (e.value == null)    
                return true;    
    return false;    
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值