请教HashSet中remove方法的一些问题

SharpCoder 2009-04-10 08:08:58
import java.util.HashSet;

class Man
{
public String name;
public int age;
public Man(String n, int a)
{
name = n;
age = a;
}

public int hashCode()
{
return name.hashCode() + age;

}

public boolean equals(Object o)
{
boolean result = false;

if (o == null)
result = false;

if (o instanceof Man)
{
Man s = (Man)o;
if ((s.age == this.age) && s.name.equals(this.name))
result = true;
}

return result;

}

}
public class TestHashCode
{
public static void main(String args[])
{

HashSet<Man> hs = new HashSet<Man>();

Man m1 = new Man("Jimmy", 27);
Man m2 = new Man("Jimmy", 28);
Man m3 = new Man("Jimmy", 29);

hs.add(m1);
hs.add(m2);
hs.add(m3);

for (Man m: hs)
System.out.println(m.name + ":" + m.age);

m1.age = 30;
m2.age = 30;
m3.age = 30;

hs.remove(m1);

for (Man m: hs)
System.out.println(m.name + ":" + m.age);


}
}
输出结果很让我诧异:

Jimmy:27
Jimmy:28
Jimmy:29
Jimmy:30
Jimmy:30
Jimmy:30

HashSet在存储element之前,需要检查唯一性。但加入以后,却又可以被修改。
如上面,我如果修改了内容是的这些元素变成相同了,hashset会马上检查吗?我猜测应该不会。

但是按照上面的,我去remove,为什么一个也不成功呢?

我觉得我可能对hashcode理解有问题。。。

...全文
620 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
ysj300 2010-09-08
  • 打赏
  • 举报
回复
前面回答的有点模糊,很多人会问为什么前后的两个hashcode不同呢,不是后面的age都是30不是。
这个我们就要从HashSet的存储方式来说了,HashSet内部其实是个HashMap,而HashMap中可以看成是两个对应的数组,一个代表Key,一个代表Value,在HashSet中Value不使用,所有我们可以看成HashSet底层其实是一个数组存储,而他的数据存储,是散列的存储,根据数据的hashcode然后经过一系列的换算,得到一个int值
对应到这个数据的位置,这个时候位置就是数据的存储位置,m1存储进去的时候有根据age=27来算存储的位置,
而当age=30后,他的hashcode已经发生改变,但是在HashSet存储的位置中他是没有变化的,这个时候我们要remove(m1)因为hashcode是不同的,找不到他,所有remove不了。
说了可能有点乱,不知道大家明白。
kahnnash 2010-09-08
  • 打赏
  • 举报
回复
添加时先比较hashCode,没有相等的,直接加入,有相等的,进入此相等队列, 再看是否连等,有连等舍弃,没有连等 最后看euqals(),有true 舍弃,没有加入。


删除时顺序一样。。


Man类要重载hashCode() equals()方法


09年的帖子拿出来忽悠人
bekeer 2010-09-08
  • 打赏
  • 举报
回复
我靠 以后还得看着点发帖时间 谁这么牛逼 从海底捞上来了
ysj300 2010-09-08
  • 打赏
  • 举报
回复
楼上,我觉得我上面都写的很清楚了,不明白呢?
sun_abc 2010-09-08
  • 打赏
  • 举报
回复
1,不修改跟hashcode有关的属性,操作是没有问题的。
2,当age=30后,m1的hashcode确实改变了,但是遍历HashSet时,里面m1的hashcode跟改变属性后的一样,为什么不能删除呢?如果再设置age=27,却是可以删除的。
3,或者直接remvoe(new Man("Jimmy", 30)),这里的元素的hashcode、equals与m1都相等吧,可还是删不掉。
这个应该跟HashMap里的hashcode机制有关,请高手指点。
ysj300 2010-09-08
  • 打赏
  • 举报
回复
“这么简单的东西就不要拿出现问显了”,这句话写错了,我把问题看的简单了一开始。
ysj300 2010-09-08
  • 打赏
  • 举报
回复
这么简单的东西就不要拿出现问显了,看看HashSet中remove的源码。
源码当中有先是对hashCode进行了比较,然后又进行了equals比较。
而你这个程序当中,首先HasheSet存了Man m1 = new Man("Jimmy", 27);
age = 27,然后age = 30,测试了一个下这里会产生使得m1的前后两个hashCode码不相同。
把hashCode方法改为:
public int hashCode() {
return name.hashCode();
}
即可,我已经测试,贴主可以试试。
awusoft 2010-09-07
  • 打赏
  • 举报
回复
我晕,居然是09年的帖子~~~~
awusoft 2010-09-07
  • 打赏
  • 举报
回复
移除之前是一个找的过程,怎么找?就是通过hash(hasCode)来找.每个的hashCode都不相同,所以找不到对象,也就是移除不成功.
awusoft 2010-09-07
  • 打赏
  • 举报
回复
肯定不成功啊....
你修改了一个age,那么所有的hashCode和添加前进之前都不相同吧?你怎么可能找到对象呢?


前是Jimmy+27,28,29
移除的时候是Jimmy+30,与任何一个都不相同,怎么能移除成功呢?
nash603 2010-09-07
  • 打赏
  • 举报
回复
按照我的理解:
第一次执行men.add(m1)时,假设JVM计算出m1的哈希值为C1,然后根据哈希值C1把m1存放到位置P1;
执行men.remove(m1)时,m1的age被改为30,JVM计算出m1的哈希值为C2,根据此哈希值,JVM会到位置P2去找m1,但是m1真正的存放位置是P1,因此无法找到m1,故删除失败
LingTianlan 2010-09-06
  • 打赏
  • 举报
回复
HashSet内部是用HashMap来管理元素的,JDK6中HashMap.class的remove内容如下:

int hash = (key == null) ? 0 : hash(key.hashCode());
int i = indexFor(hash, table.length);
Entry<K,V> prev = table[i];
Entry<K,V> e = prev;

while (e != null) {
Entry<K,V> next = e.next;
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
modCount++;
size--;
if (prev == e)
table[i] = next;
else
prev.next = next;
e.recordRemoval(this);
return e;
}
prev = e;
e = next;
}


可见若要判断为相同元素,首先应该保证hashCode相同,然后再保证equals方法返回true。
SharpCoder 2009-04-11
  • 打赏
  • 举报
回复
回4楼。

code1处的hashcode改变了那是肯定的,因为我该写了hashcode函数了。

问题时既然m1,m2,m3三个对象都已经改变了,他的hashcode都应该跟着改变。

为什么后来remove的时候 还采用原来的hashcode作比较呢?难道元来的hashcode已经存放在某个地方了?


下面的文字从API文档中拷贝:
==================

remove
public boolean remove(Object o)如果指定元素存在于此 set 中,则将其移除。更确切地讲,如果此 set 包含一个满足 (o==null ? e==null : o.equals(e)) 的元素 e,则将其移除。如果此 set 已包含该元素,则返回 true(或者:如果此 set 因调用而发生更改,则返回 true)。(一旦调用返回,则此 set 不再包含该元素)。

如果按照这个规则,应该能够删除的。
chihz3800 2009-04-10
  • 打赏
  • 举报
回复
import java.util.*;
public class Test {

public static void main(String[]args){
HashSet<Man>men=new HashSet<Man>();

Man m1=new Man("Sam",21);
Man m2=new Man("Sam",22);
Man m3=new Man("Sam",23);

men.add(m1);
men.add(m2);
men.add(m3);

System.out.println(m1.hashCode()); //code1

for(Man temp:men){
System.out.println(temp);
}

m1.setAge(30);
m2.setAge(30);
m3.setAge(30);

System.out.println(m1.hashCode()); //code2

System.out.println("After setting:");

men.remove(m1);

for(Man temp:men){
System.out.println(temp);
}

}
}

我测试了code1处的hash值与code2处的hash值是不同的,remove()在判断时应该是用code1处的hash值与code2处的hash值进行对比,发现不一致导致无法删除吧,我只是猜测这样...

SharpCoder 2009-04-10
  • 打赏
  • 举报
回复
回一楼。

我修改如下:
m1.age = 30;
m2.age = 30;
m3.age = 30;



再调用
hs.remove(m1);

为什么仍然一个也没有删除呢?



possibleonline 2009-04-10
  • 打赏
  • 举报
回复
期待高手
chihz3800 2009-04-10
  • 打赏
  • 举报
回复
我想Set仅仅是在添加元素的时候进行唯一性检查吧,然后我们通过引用对其进行修改时不进行检查,remove()删除时依靠equals()来判断目标,因此无法删除...我看了以下Java数据结构的资料觉得应该是这样...

62,614

社区成员

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

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