找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 17460|回复: 98

Java集合框架源码剖析:HashSet 和 HashMap

  [复制链接]

10

主题

433

回帖

2689

积分

高级会员

积分
2689
发表于 2018-9-11 08:13:13 | 显示全部楼层 |阅读模式

总体介绍

之所以把HashSet和HashMap放在一起讲解,是因为二者在Java里有着相同的实现,前者仅仅是对后者做了一层包装,也就是说HashSet里面有一个HashMap(适配器模式)。因此本文将重点分析HashMap。


HashMap实现了Map接口,允许放入null元素,除该类未实现同步外,其余跟Hashtable大致相同,跟TreeMap不同,该容器不保证元素顺序,根据需要该容器可能会对元素重新哈希,元素的顺序也会被重新打散,因此不同时间迭代同一个HashMap的顺序可能会不同。


根据对冲突的处理方式不同,哈希表有两种实现方式,一种开放地址方式(Open addressing),另一种是冲突链表方式(Separate chaining with linked lists)。Java HashMap采用的是冲突链表方式。





从上图容易看出,如果选择合适的哈希函数,put()和get()方法可以在常数时间内完成。但在对HashMap进行迭代时,需要遍历整个table以及后面跟的冲突链表。因此对于迭代比较频繁的场景,不宜将HashMap的初始大小设的过大。


有两个参数可以影响HashMap的性能:初始容量(inital capacity)和负载系数(load factor)。初始容量指定了初始table的大小,负载系数用来指定自动扩容的临界值。当entry的数量超过capacity*load_factor时,容器将自动扩容并重新哈希。对于插入元素较多的场景,将初始容量设大可以减少重新哈希的次数。


将对向放入到HashMap或HashSet中时,有两个方法需要特别关心:hashCode()和equals()。hashCode()方法决定了对象会被放到哪个bucket里,当多个对象的哈希值冲突时,equals()方法决定了这些对象是否是“同一个对象”。所以,如果要将自定义的对象放入到HashMap或HashSet中,需要@Override hashCode()和equals()方法。


方法剖析


get()


get(Object key)方法根据指定的key值返回对应的value,该方法调用了getEntry(Object key)得到相应的entry,然后返回entry.getValue()。因此getEntry()是算法的核心。


算法思想是首先通过hash()函数得到对应bucket的下标,然后依次遍历冲突链表,通过key.equals(k)方法来判断是否是要找的那个entry。





上图中hash(k)&(table.length-1)等价于hash(k)%table.length,原因是HashMap要求table.length必须是2的指数,因此table.length-1就是二进制低位全是1,跟hash(k)相与会将哈希值的高位全抹掉,剩下的就是余数了。


[color=#999999 !important]//getEntry()方法
[color=#800080 !important]final[color=#006FE0 !important] [color=#002D7A !important]Entry[color=#006FE0 !important] [color=teal !important]getEntry[color=#333333 !important]([color=#800080 !important]Object[color=#006FE0 !important] [color=#002D7A !important]key[color=#333333 !important])[color=#006FE0 !important] [color=#333333 !important]{
[color=#006FE0 !important]    [color=#333333 !important]......
[color=#006FE0 !important]    [color=#800080 !important]int[color=#006FE0 !important] [color=#002D7A !important]hash[color=#006FE0 !important] = [color=#333333 !important]([color=#002D7A !important]key[color=#006FE0 !important] == [color=#800080 !important]null[color=#333333 !important])[color=#006FE0 !important] [color=#333333 !important]?[color=#006FE0 !important] [color=#009999 !important]0[color=#006FE0 !important] : [color=teal !important]hash[color=#333333 !important]([color=#002D7A !important]key[color=#333333 !important]);
[color=#006FE0 !important]    for[color=#006FE0 !important] [color=#333333 !important]([color=#002D7A !important]Entry[color=#006FE0 !important] [color=#002D7A !important]e[color=#006FE0 !important] = [color=#002D7A !important]table[color=#333333 !important][[color=#002D7A !important]hash[color=#006FE0 !important]&[color=#333333 !important]([color=#002D7A !important]table[color=#333333 !important].[color=#002D7A !important]length[color=#006FE0 !important]-[color=#009999 !important]1[color=#333333 !important])];[color=#999999 !important]//得到冲突链表
[color=#006FE0 !important]         [color=#002D7A !important]e[color=#006FE0 !important] != [color=#800080 !important]null[color=#333333 !important];[color=#006FE0 !important] [color=#002D7A !important]e[color=#006FE0 !important] = [color=#002D7A !important]e[color=#333333 !important].[color=#002D7A !important]next[color=#333333 !important])[color=#006FE0 !important] [color=#333333 !important]{[color=#999999 !important]//依次遍历冲突链表中的每个entry
[color=#006FE0 !important]        [color=#800080 !important]Object[color=#006FE0 !important] [color=#002D7A !important]k[color=#333333 !important];
[color=#006FE0 !important]        [color=#999999 !important]//依据equals()方法判断是否相等
[color=#006FE0 !important]        if[color=#006FE0 !important] [color=#333333 !important]([color=#002D7A !important]e[color=#333333 !important].[color=#002D7A !important]hash[color=#006FE0 !important] == [color=#002D7A !important]hash[color=#006FE0 !important] &&
[color=#006FE0 !important]            [color=#333333 !important](([color=#002D7A !important]k[color=#006FE0 !important] = [color=#002D7A !important]e[color=#333333 !important].[color=#002D7A !important]key[color=#333333 !important])[color=#006FE0 !important] == [color=#002D7A !important]key[color=#006FE0 !important] || [color=#333333 !important]([color=#002D7A !important]key[color=#006FE0 !important] != [color=#800080 !important]null[color=#006FE0 !important] && [color=#002D7A !important]key[color=#333333 !important].[color=teal !important]equals[color=#333333 !important]([color=#002D7A !important]k[color=#333333 !important]))))
[color=#006FE0 !important]            return[color=#006FE0 !important] [color=#002D7A !important]e[color=#333333 !important];
[color=#006FE0 !important]    [color=#333333 !important]}
[color=#006FE0 !important]    return[color=#006FE0 !important] [color=#800080 !important]null[color=#333333 !important];
[color=#333333 !important]}


put()


put(K key, V value)方法是将指定的key, value对添加到map里。该方法首先会对map做一次查找,看是否包含该元组,如果已经包含则直接返回,查找过程类似于getEntry()方法;如果没有找到,则会通过addEntry(int hash, K key, V value, int bucketIndex)方法插入新的entry,插入方式为头插法。





[color=#999999 !important]//addEntry()
[color=#800080 !important]void[color=#006FE0 !important] [color=teal !important]addEntry[color=#333333 !important]([color=#800080 !important]int[color=#006FE0 !important] [color=#002D7A !important]hash[color=#333333 !important],[color=#006FE0 !important] K[color=#006FE0 !important] [color=#002D7A !important]key[color=#333333 !important],[color=#006FE0 !important] V[color=#006FE0 !important] [color=#002D7A !important]value[color=#333333 !important],[color=#006FE0 !important] [color=#800080 !important]int[color=#006FE0 !important] [color=#002D7A !important]bucketIndex[color=#333333 !important])[color=#006FE0 !important] [color=#333333 !important]{
[color=#006FE0 !important]    if[color=#006FE0 !important] [color=#333333 !important](([color=#002D7A !important]size[color=#006FE0 !important] >= [color=#002D7A !important]threshold[color=#333333 !important])[color=#006FE0 !important] && [color=#333333 !important]([color=#800080 !important]null[color=#006FE0 !important] != [color=#002D7A !important]table[color=#333333 !important][[color=#002D7A !important]bucketIndex[color=#333333 !important]]))[color=#006FE0 !important] [color=#333333 !important]{
[color=#006FE0 !important]        [color=teal !important]resize[color=#333333 !important]([color=#009999 !important]2[color=#006FE0 !important] * [color=#002D7A !important]table[color=#333333 !important].[color=#002D7A !important]length[color=#333333 !important]);[color=#999999 !important]//自动扩容,并重新哈希
[color=#006FE0 !important]        [color=#002D7A !important]hash[color=#006FE0 !important] = [color=#333333 !important]([color=#800080 !important]null[color=#006FE0 !important] != [color=#002D7A !important]key[color=#333333 !important])[color=#006FE0 !important] [color=#333333 !important]?[color=#006FE0 !important] [color=teal !important]hash[color=#333333 !important]([color=#002D7A !important]key[color=#333333 !important])[color=#006FE0 !important] : [color=#009999 !important]0[color=#333333 !important];
[color=#006FE0 !important]        [color=#002D7A !important]bucketIndex[color=#006FE0 !important] = [color=#002D7A !important]hash[color=#006FE0 !important] & [color=#333333 !important]([color=#002D7A !important]table[color=#333333 !important].[color=#002D7A !important]length[color=#006FE0 !important]-[color=#009999 !important]1[color=#333333 !important]);[color=#999999 !important]//hash%table.length
[color=#006FE0 !important]    [color=#333333 !important]}
[color=#006FE0 !important]    [color=#999999 !important]//在冲突链表头部插入新的entry
[color=#006FE0 !important]    [color=#002D7A !important]Entry[color=#006FE0 !important] [color=#002D7A !important]e[color=#006FE0 !important] = [color=#002D7A !important]table[color=#333333 !important][[color=#002D7A !important]bucketIndex[color=#333333 !important]];
[color=#006FE0 !important]    [color=#002D7A !important]table[color=#333333 !important][[color=#002D7A !important]bucketIndex[color=#333333 !important][color=#006FE0 !important] = new[color=#006FE0 !important] [color=#002D7A !important]Entry[color=#006FE0 !important][color=#333333 !important]([color=#002D7A !important]hash[color=#333333 !important],[color=#006FE0 !important] [color=#002D7A !important]key[color=#333333 !important],[color=#006FE0 !important] [color=#002D7A !important]value[color=#333333 !important],[color=#006FE0 !important] [color=#002D7A !important]e[color=#333333 !important]);
[color=#006FE0 !important]    [color=#002D7A !important]size[color=#006FE0 !important]++[color=#333333 !important];
[color=#333333 !important]}


remove()


remove(Object key)的作用是删除key值对应的entry,该方法的具体逻辑是在removeEntryForKey(Object key)里实现的。removeEntryForKey()方法会首先找到key值对应的entry,然后删除该entry(修改链表的相应指针)。查找过程跟getEntry()过程类似。







[color=#999999 !important]//removeEntryForKey()
[color=#800080 !important]final[color=#006FE0 !important] [color=#002D7A !important]Entry[color=#006FE0 !important] [color=teal !important]removeEntryForKey[color=#333333 !important]([color=#800080 !important]Object[color=#006FE0 !important] [color=#002D7A !important]key[color=#333333 !important])[color=#006FE0 !important] [color=#333333 !important]{
[color=#006FE0 !important]    [color=#333333 !important]......
[color=#006FE0 !important]    [color=#800080 !important]int[color=#006FE0 !important] [color=#002D7A !important]hash[color=#006FE0 !important] = [color=#333333 !important]([color=#002D7A !important]key[color=#006FE0 !important] == [color=#800080 !important]null[color=#333333 !important])[color=#006FE0 !important] [color=#333333 !important]?[color=#006FE0 !important] [color=#009999 !important]0[color=#006FE0 !important] : [color=teal !important]hash[color=#333333 !important]([color=#002D7A !important]key[color=#333333 !important]);
[color=#006FE0 !important]    [color=#800080 !important]int[color=#006FE0 !important] [color=#002D7A !important]i[color=#006FE0 !important] = [color=teal !important]indexFor[color=#333333 !important]([color=#002D7A !important]hash[color=#333333 !important],[color=#006FE0 !important] [color=#002D7A !important]table[color=#333333 !important].[color=#002D7A !important]length[color=#333333 !important]);[color=#999999 !important]//hash&(table.length-1)
[color=#006FE0 !important]    [color=#002D7A !important]Entry[color=#006FE0 !important] [color=#002D7A !important]prev[color=#006FE0 !important] = [color=#002D7A !important]table[color=#333333 !important][[color=#002D7A !important]i[color=#333333 !important]];[color=#999999 !important]//得到冲突链表
[color=#006FE0 !important]    [color=#002D7A !important]Entry[color=#006FE0 !important] [color=#002D7A !important]e[color=#006FE0 !important] = [color=#002D7A !important]prev[color=#333333 !important];
[color=#006FE0 !important]    while[color=#006FE0 !important] [color=#333333 !important]([color=#002D7A !important]e[color=#006FE0 !important] != [color=#800080 !important]null[color=#333333 !important])[color=#006FE0 !important] [color=#333333 !important]{[color=#999999 !important]//遍历冲突链表
[color=#006FE0 !important]        [color=#002D7A !important]Entry[color=#006FE0 !important] [color=#002D7A !important]next[color=#006FE0 !important] = [color=#002D7A !important]e[color=#333333 !important].[color=#002D7A !important]next[color=#333333 !important];
[color=#006FE0 !important]        [color=#800080 !important]Object[color=#006FE0 !important] [color=#002D7A !important]k[color=#333333 !important];
[color=#006FE0 !important]        if[color=#006FE0 !important] [color=#333333 !important]([color=#002D7A !important]e[color=#333333 !important].[color=#002D7A !important]hash[color=#006FE0 !important] == [color=#002D7A !important]hash[color=#006FE0 !important] &&
[color=#006FE0 !important]            [color=#333333 !important](([color=#002D7A !important]k[color=#006FE0 !important] = [color=#002D7A !important]e[color=#333333 !important].[color=#002D7A !important]key[color=#333333 !important])[color=#006FE0 !important] == [color=#002D7A !important]key[color=#006FE0 !important] || [color=#333333 !important]([color=#002D7A !important]key[color=#006FE0 !important] != [color=#800080 !important]null[color=#006FE0 !important] && [color=#002D7A !important]key[color=#333333 !important].[color=teal !important]equals[color=#333333 !important]([color=#002D7A !important]k[color=#333333 !important]))))[color=#006FE0 !important] [color=#333333 !important]{[color=#999999 !important]//找到要删除的entry
[color=#006FE0 !important]            [color=#002D7A !important]modCount[color=#006FE0 !important]++[color=#333333 !important];[color=#006FE0 !important] [color=#002D7A !important]size[color=#006FE0 !important]--[color=#333333 !important];
[color=#006FE0 !important]            if[color=#006FE0 !important] [color=#333333 !important]([color=#002D7A !important]prev[color=#006FE0 !important] == [color=#002D7A !important]e[color=#333333 !important])[color=#006FE0 !important] [color=#002D7A !important]table[color=#333333 !important][[color=#002D7A !important]i[color=#333333 !important][color=#006FE0 !important] = [color=#002D7A !important]next[color=#333333 !important];[color=#999999 !important]//删除的是冲突链表的第一个entry
[color=#006FE0 !important]            else[color=#006FE0 !important] [color=#002D7A !important]prev[color=#333333 !important].[color=#002D7A !important]next[color=#006FE0 !important] = [color=#002D7A !important]next[color=#333333 !important];
[color=#006FE0 !important]            return[color=#006FE0 !important] [color=#002D7A !important]e[color=#333333 !important];
[color=#006FE0 !important]        [color=#333333 !important]}
[color=#006FE0 !important]        [color=#002D7A !important]prev[color=#006FE0 !important] = [color=#002D7A !important]e[color=#333333 !important];[color=#006FE0 !important] [color=#002D7A !important]e[color=#006FE0 !important] = [color=#002D7A !important]next[color=#333333 !important];
[color=#006FE0 !important]    [color=#333333 !important]}
[color=#006FE0 !important]    return[color=#006FE0 !important] [color=#002D7A !important]e[color=#333333 !important];
[color=#333333 !important]}


HashSet


前面已经说过HashSet是对HashMap的简单包装,对HashSet的函数调用都会转换成合适的HashMap方法,因此HashSet的实现非常简单,只有不到300行代码。这里不再赘述。


[color=#999999 !important]//HashSet是对HashMap的简单包装
[color=#800080 !important]public[color=#006FE0 !important] [color=#800080 !important]class[color=#006FE0 !important] [color=teal !important]HashSet[color=#006FE0 !important]
[color=#333333 !important]{
[color=#006FE0 !important]    [color=#333333 !important]......
[color=#006FE0 !important]    [color=#800080 !important]private[color=#006FE0 !important] [color=teal !important]transient [color=#002D7A !important]HashMap[color=#006FE0 !important] [color=#002D7A !important]map[color=#333333 !important];[color=#999999 !important]//HashSet里面有一个HashMap
[color=#006FE0 !important]    [color=#999999 !important]// Dummy value to associate with an Object in the backing Map
[color=#006FE0 !important]    [color=#800080 !important]private[color=#006FE0 !important] [color=#800080 !important]static[color=#006FE0 !important] [color=#800080 !important]final[color=#006FE0 !important] [color=#800080 !important]Object[color=#006FE0 !important] [color=#002D7A !important]PRESENT[color=#006FE0 !important] = new[color=#006FE0 !important] [color=#800080 !important]Object[color=#333333 !important]();
[color=#006FE0 !important]    [color=#800080 !important]public[color=#006FE0 !important] [color=teal !important]HashSet[color=#333333 !important]()[color=#006FE0 !important] [color=#333333 !important]{
[color=#006FE0 !important]        [color=#002D7A !important]map[color=#006FE0 !important] = new[color=#006FE0 !important] [color=#002D7A !important]HashMap[color=#006FE0 !important][color=#333333 !important]();
[color=#006FE0 !important]    [color=#333333 !important]}
[color=#006FE0 !important]    [color=#333333 !important]......
[color=#006FE0 !important]    [color=#800080 !important]public[color=#006FE0 !important] [color=#800080 !important]boolean[color=#006FE0 !important] [color=teal !important]add[color=#333333 !important](E[color=#006FE0 !important] [color=#002D7A !important]e[color=#333333 !important])[color=#006FE0 !important] [color=#333333 !important]{[color=#999999 !important]//简单的方法转换
[color=#006FE0 !important]        return[color=#006FE0 !important] [color=#002D7A !important]map[color=#333333 !important].[color=teal !important]put[color=#333333 !important]([color=#002D7A !important]e[color=#333333 !important],[color=#006FE0 !important] [color=#002D7A !important]PRESENT[color=#333333 !important])[color=#006FE0 !important]==[color=#800080 !important]null[color=#333333 !important];
[color=#006FE0 !important]    [color=#333333 !important]}
[color=#006FE0 !important]    [color=#333333 !important]......
[color=#333333 !important]}

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
工控课堂 www.gkket.com

0

主题

426

回帖

2654

积分

高级会员

积分
2654
发表于 2018-9-19 21:24:44 | 显示全部楼层
大家都不容易!
工控课堂 www.gkket.com

0

主题

443

回帖

2470

积分

高级会员

积分
2470
发表于 2018-9-20 04:47:11 | 显示全部楼层
楼主您的技术水准,我最服你,其他都是浮云
工控课堂 www.gkket.com

0

主题

405

回帖

2356

积分

高级会员

积分
2356
发表于 2018-9-21 18:43:49 | 显示全部楼层
无私奉献,好工控人,32个赞送给你!!
工控课堂 www.gkket.com

9

主题

412

回帖

2406

积分

高级会员

积分
2406
发表于 2018-9-21 20:10:28 | 显示全部楼层
真是难得给力的帖子啊。
工控课堂 www.gkket.com

9

主题

412

回帖

2406

积分

高级会员

积分
2406
发表于 2018-9-21 20:16:29 | 显示全部楼层
感恩无私的分享与奉献
工控课堂 www.gkket.com

8

主题

422

回帖

2820

积分

高级会员

积分
2820
发表于 2018-9-21 20:41:33 | 显示全部楼层
真是难得给力的帖子啊。
工控课堂 www.gkket.com

15

主题

405

回帖

2318

积分

高级会员

积分
2318
发表于 2018-9-24 14:11:23 | 显示全部楼层
楼主加油,我们都看好你哦。
工控课堂 www.gkket.com

0

主题

439

回帖

2772

积分

高级会员

积分
2772
发表于 2018-9-25 00:37:30 | 显示全部楼层
真是难得给力的帖子啊。
工控课堂 www.gkket.com

11

主题

368

回帖

2645

积分

高级会员

积分
2645
发表于 2018-9-28 05:43:53 | 显示全部楼层
看了楼主的帖子,不由得精神一振,豁然开朗,牛掰
工控课堂 www.gkket.com
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐上一条 /1 下一条

QQ|手机版|免责声明|本站介绍|工控课堂 ( 沪ICP备20008691号-1 )

GMT+8, 2025-12-22 13:31 , Processed in 0.109976 second(s), 30 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表