HashMap的问题

结贴是美德 2012-02-03 08:27:15
刚刚在看HashMap的put方法的源码时有一处没看明白:


public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { //①
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}

modCount++;
addEntry(hash, key, value, i);
return null;
}


既然已经定位到table数组的i处,那桶里的元素的hash一定是和参数key的hash是相等的吧?为何还要判断e.hash == hash?
...全文
104 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
MiceRice 2012-02-04
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 ihtml 的回复:]
i相等,hashcode不一定相等。。恩 我貌似明白了。但是,既然在同一个桶中判断key相等不相等的时候hashcode帮不上忙,那直接靠equals方法不就行了?我一直被教育:equals为true时hashcode一定相等。。。
[/Quote]

因为hash是int型,这个e.hash == hash的CPU计算效率高啊。

如果 e.hash != hash,后面费时费力的equals()方法就不需要执行了,多好。
neunewpeople 2012-02-04
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 ihtml 的回复:]
i相等,hashcode不一定相等。。恩 我貌似明白了。但是,既然在同一个桶中判断key相等不相等的时候hashcode帮不上忙,那直接靠equals方法不就行了?我一直被教育:equals为true时hashcode一定相等。。。
[/Quote]
equals是判断对象的值是否相同,虽然hashcode是相同的值,但未必是同一个对象,我的理解
neunewpeople 2012-02-04
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 ldh911 的回复:]
楼主,看得挺仔细,值得表扬,但代码没看清楚吧?另外估计也把大学教的数据结构忘了。

◎ 首先有个背景知识说明下:
Hash是无法保证不重复的,因为是Map本身是有容量的,也就是散列映射空间是有限的,Hash中存储的元素个数约接近散列的映射空间,重复度越高;这也是为什么HashMap构造函数有个参数,叫做“loadFactor”。
不扯远了,那么在Hash发生冲突的情况,需要另外找一个位置来……
[/Quote]
讲得很细,学习啦
结贴是美德 2012-02-04
  • 打赏
  • 举报
回复
i相等,hashcode不一定相等。。恩 我貌似明白了。但是,既然在同一个桶中判断key相等不相等的时候hashcode帮不上忙,那直接靠equals方法不就行了?我一直被教育:equals为true时hashcode一定相等。。。
爱摸鱼de老邪 2012-02-04
  • 打赏
  • 举报
回复
楼上这个讲解很生动,强烈的顶支持一下。数据结构书里有散列的专题,楼主好好看看就明白了。
MiceRice 2012-02-04
  • 打赏
  • 举报
回复
楼主,看得挺仔细,值得表扬,但代码没看清楚吧?另外估计也把大学教的数据结构忘了。

◎ 首先有个背景知识说明下:
Hash是无法保证不重复的,因为是Map本身是有容量的,也就是散列映射空间是有限的,Hash中存储的元素个数约接近散列的映射空间,重复度越高;这也是为什么HashMap构造函数有个参数,叫做“loadFactor”。
不扯远了,那么在Hash发生冲突的情况,需要另外找一个位置来存储,常规而言有两种策略:1、重算一个新Hash;2、取下一个。一般用策略2。

◎ 接下来第二个问题:程序中的 int i 是啥?
i 是hashcode在映射空间上的位置,简单点理解可以认为:
int i = hashcode % mapcapacity;
如果极端点,mapcapacity只有2的时候,那么i只能==0或者1
这意味着什么?意味着即便i相等,hashcode未必相等。


好了,有了上述背景知识再研读代码就不难了:
◎ for (Entry<K,V> e = table[i]; e != null; e = e.next) 究竟在干啥?
—— 答:在获取hashcode经过映射后Map存储空间指定位置的内容;
◎ if (e.hash == hash && ((k = e.key) == key || key.equals(k))) 在干啥?为啥要先e.hash == hash?
—— 答:在判断这个位置的key,究竟是不是等于参数传进来的key(注意不同对象的hashcode是可以相等的,其所在的映射位置i更容易重复)
—— 答:因为hash是int型,这个e.hash == hash的CPU计算效率高啊
◎ 上面那个if失败了咋整?
—— 答:注意for循环(e = e.next),失败了就尝试去找下一个存储空间。
◎ 我X,HashMap如果发生大量这种i重复冲突的话,性能不是很烂?
—— 答:是的,所以注意loadFactor不能太大,否则害死人;那么就给个很小很小的数?呃,你也得考虑下内存开销吧。。。
结贴是美德 2012-02-04
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 ldh911 的回复:]

引用 5 楼 ihtml 的回复:
i相等,hashcode不一定相等。。恩 我貌似明白了。但是,既然在同一个桶中判断key相等不相等的时候hashcode帮不上忙,那直接靠equals方法不就行了?我一直被教育:equals为true时hashcode一定相等。。。


因为hash是int型,这个e.hash == hash的CPU计算效率高啊。

如果 e.hash != ha……
[/Quote]

我c 我cccc 原来是这样。。。。
fairjm 2012-02-03
  • 打赏
  • 举报
回复
中文版文档中有:
注:将可变对象用作映射键时必须格外小心。当对象是映射中某个键时,如果以影响 equals 比较的方式更改了对象的值,则映射的行为将是不确定的。此项禁止的一种特殊情况是不允许某个映射将自身作为一个键包含。虽然允许某个映射将自身作为值包含,但请格外小心:在这样的映射上 equals 和 hashCode 方法的定义将不再是明确的。


如果Key是引用类型的 比如有两个个类的实例 其equals返回相同
不比较hashcode的话 可能会不同的两个实例 使用相同的一个key

我说不清楚饿 高手请指正
龙四 2012-02-03
  • 打赏
  • 举报
回复
hashmap不是线程安全的,如果没有记错,在put的时候如果另一个put也在进行却没有同步,就有可能rehash(应该在addEntry(hash, key, value, i);里面),rehash会导致i所在的位置的hashcode不等于之前的hashcode

62,615

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧