面试的小伙伴经常被问到了解hashmap吗,hashmap怎么实现的,看过hashmap的源码吗?大多数工作时间不长的哥们,基本上对于hashmap只局限于会用,只会调调SKD,也许对工作多年的哥们来说,对于hashmap的底层原来也是一知半解,并不能很好的理解,今天就深入来讲解下最常用的hashmap
第一介绍几个关键属性
static final int DEFAULT_INITIAL_CAPACITY =1 <<4;// aka 16 默认初始化容量(这个是在jdk1.8中是定死的,无论你构造函数是否传入参数,jdk并不会根据你传入的参数初始化,至于为什么定死,后面在解读)
static final int MAXIMUM_CAPACITY =1 <<30;// 最大容量很大的一个值
static final float DEFAULT_LOAD_FACTOR =0.75f;//装填因子//这个参数默认0.75 这个参数,影响haskmap扩容。可以自己定义,0.75目前是最优,不建议自己设置装填因子
threshold //扩容阈值 根据 DEFAULT_INITIAL_CAPACITY*DEFAULT_LOAD_FACTOR 算出
1.默认无参构造函数
把默认装填因子0.75复制成员变量
2.有参构造函数
有参数构造传入容量,和装填因子,发现并没有把容量赋值全局变量,只赋值了装填因子,初始化容量只用来在最后一步计算了扩容的阈值并赋值给全局变量。容器数量还是默认的16
3. put方法
首先对key做了hash,为什么对key做hash计算,第一是为了计算桶装数组索引下标,第二减少hash冲突,降低碰撞概率
取key-hashcode,相当于做了个二次哈希操作,为什么这么设计,是为了降低哈希冲突。目的还是为了,减少碰撞,让元素均与的分布在桶装数组中
对key进行hash计算过后,第二部就是计算桶装数组下标。怎么计算的呢?
就是默认(桶装数组长度-1)然后和key进行的hash运算进行位运算。
这时候来解释下为什么桶装数组容量,JDK不让程序员自己定义,原来就在这,就是因为要计算桶装数组的元素索引下标,那为什么要数组长度要减去一呢?其实还是为了减少hash冲突,降低碰撞概率,桶装数组的长度,如果让程序员自己定义,有可能长度就不是2的幂,然后在减去1,就是偶数在和key的hash计算,发生冲突概率非常高,所以为了避免这个问题,JDK就不会使用程序员自己定义的初始化容量。
那为什么偶数和hash做位运算冲突概率就高呢,因为偶数的二进制位0多发生与运算碰撞几率高。
第三布,计算下标出来后,看下标出有没有元素,如果没有元素,则往桶装元素中添加元素,如果有元素,判断老元素的Hash与新元素的hash是否一样,并且key的值是否一样,如果一样,就用新值覆盖原有老的值。如果不一样。判断老节点是否是红黑树节点,把新节点放入到红黑树当中。如果不是红黑树节点,把新节点链到老节点后面。然后判断是否到转红黑树的阈值。如果到了,把当前节点转成红黑树
get方法
与put方法步骤类似就不重复了!