您的位置:首页 > 编程语言 > Java开发

【JDk源码解析之三】HashMap源码解析

2017-04-15 19:27 218 查看

1.HashMap的实现了Map接口,继承于AbstractMap



2.我们常说HashMap是数组加链表的实现形式,那么就具体来看看。HashMap的主要属性定义如下图。先说数组+链表,数组只的就是table[]数组,这个数组是Entry类型的,而Entry类类型于我们常用的Node节点,Entry有(k,v,next)属性。默认初始化容量为1<<4,也就是16,同时还定义了map的最大容量1<<30,负载因子是0.75,threshold存放的是需要扩容的临界值,threshold=Capacity*loadFactor。



3.具体方法分析

3.1 构造方法。

三种构造方法,但最终都是调用两个参数的构造方法,如果声明map对象,没有显示的指定大小,那么就默认构造容量16的map。而这个时候,注意看threshold是等于16的,并不是16*0.75=12,这点注意。也就是说,初始化时候,threshold并不是12,而是16。那么什么时候threshold才等于capacit*loadFactor呢?就是第一次put数据时候,会将threshold改变成12,具体见下文。


3.2增加元素的方法:put()

增加元素的时候,先判断table数组是否是空,如果是空,那么就去执行inflateTable方法。这个方法会将table的大小调整为传入的参数。定义map初始化时候,threshold等于16,这个时候会将table的大小调整为附近的2的次幂。16正好是2的次幂。另外需要说的是,Hashmap的容量必须是2的整数次幂,如果传入的大小不是2的整数次幂,就将它调整为2的整数次幂。然后threshold就等于Capacity*loadFactor。如果key是null,那么就执行putForNullKey。进入putForNullKey,如果key是null,那么就直接就到table[0]。table[0]存放的是(hashcode%table.length
== 0)的entry和null。HashMap采用的是链地址法解决冲突,所以获取table[0]的元素后,通过next不断寻找key=null的entry.从这里看出来,map里面能存null的key,但是只能存一个,如果遍历琏地址没找到的话,那就addEntry。bucketIndex就是table数组的下标。

如果put的key不是null,那么就根据hash()函数,计算key对应的hash。接下来根据hash值,通过indexForIndex计算对应的table下标。index = h & (length-1);hash函数计算key对应的hash值的方法很有套路,在以后单独的博客会讲。算出对应的table下标后,会根据(e.hash == hash && ((k = e.key) == key || key.equals(k)))判断map里是否已经有相应的key,如果有就更新他的value。根据这个判断,如果我们自定义的一个类对象,如果要做key,那就必须覆写equals方法,同时最好复写hashcode方法。阿里面试问过我这个,当时当然一脸懵逼。



3.2 删除元素:remove()

删除元素的时候,会返回该key对应的value。整体过程删除的时候,用pre记录前一个访问的节点,然后遍历,修改pre的next'即可。



3.3获取元素:get方法



4.总结

HashMap不是线程安全的,可以存放null,底层通过链地址法解决冲突。如果自定义类对象作为key,那么这个对象必须覆盖equals方法和hashcode方法。equals方法默认比较的是两个引用的地址是不是一样,除非对象复写了equals方法,绝大部分常用的类基本都复写了equals方法。

HashMap的初始化容量是16,并且它的容量只能是2的次幂,每次扩容变为原来的2倍。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  hashmap 源码 java