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:30HashSet在存储element之前,需要检查唯一性。但加入以后,却又可以被修改。
如上面,我如果修改了内容是的这些元素变成相同了,hashset会马上检查吗?我猜测应该不会。但是按照上面的,我去remove,为什么一个也不成功呢?我觉得我可能对hashcode理解有问题
{
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:30HashSet在存储element之前,需要检查唯一性。但加入以后,却又可以被修改。
如上面,我如果修改了内容是的这些元素变成相同了,hashset会马上检查吗?我猜测应该不会。但是按照上面的,我去remove,为什么一个也不成功呢?我觉得我可能对hashcode理解有问题
解决方案 »
- 出了几个面试题,用来面试毕业生,大家看看难度是否合适?
- 用swing怎么实现像ext里面的table类型切换啊
- Myeclipse 8.5
- 是高手进来看下
- 大家看过来:【Java】字符串转换成日期型数据国际化问题
- java真的是“一次编写,到处运行吗”?看我的移植:
- 怎样在linux里配置j2sdk
- 为什么书上说swing有内建的JOptionDialog;可我的jdk内却没有
- 我是一个从没用过JAVA的菜鸟,想问一下各位JAVA有没有下面的功能。(见内容)
- 在oracle中怎么能自动加上时间?
- Pattern pt = Pattern.compile(":\\/{2}|@");正则表达式什么意思
- runtime与外部应用程序交互的问题
m1.age = 30;
m2.age = 30;
m3.age = 30;
再调用
hs.remove(m1); 为什么仍然一个也没有删除呢?
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值进行对比,发现不一致导致无法删除吧,我只是猜测这样...
下面的文字从API文档中拷贝:
==================remove
public boolean remove(Object o)如果指定元素存在于此 set 中,则将其移除。更确切地讲,如果此 set 包含一个满足 (o==null ? e==null : o.equals(e)) 的元素 e,则将其移除。如果此 set 已包含该元素,则返回 true(或者:如果此 set 因调用而发生更改,则返回 true)。(一旦调用返回,则此 set 不再包含该元素)。 如果按照这个规则,应该能够删除的。
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。
你修改了一个age,那么所有的hashCode和添加前进之前都不相同吧?你怎么可能找到对象呢?
前是Jimmy+27,28,29
移除的时候是Jimmy+30,与任何一个都不相同,怎么能移除成功呢?
第一次执行men.add(m1)时,假设JVM计算出m1的哈希值为C1,然后根据哈希值C1把m1存放到位置P1;
执行men.remove(m1)时,m1的age被改为30,JVM计算出m1的哈希值为C2,根据此哈希值,JVM会到位置P2去找m1,但是m1真正的存放位置是P1,因此无法找到m1,故删除失败
删除时顺序一样。。
Man类要重载hashCode() equals()方法
09年的帖子拿出来忽悠人
源码当中有先是对hashCode进行了比较,然后又进行了equals比较。
而你这个程序当中,首先HasheSet存了Man m1 = new Man("Jimmy", 27);
age = 27,然后age = 30,测试了一个下这里会产生使得m1的前后两个hashCode码不相同。
把hashCode方法改为:
public int hashCode() {
return name.hashCode();
}
即可,我已经测试,贴主可以试试。
这个我们就要从HashSet的存储方式来说了,HashSet内部其实是个HashMap,而HashMap中可以看成是两个对应的数组,一个代表Key,一个代表Value,在HashSet中Value不使用,所有我们可以看成HashSet底层其实是一个数组存储,而他的数据存储,是散列的存储,根据数据的hashcode然后经过一系列的换算,得到一个int值
对应到这个数据的位置,这个时候位置就是数据的存储位置,m1存储进去的时候有根据age=27来算存储的位置,
而当age=30后,他的hashcode已经发生改变,但是在HashSet存储的位置中他是没有变化的,这个时候我们要remove(m1)因为hashcode是不同的,找不到他,所有remove不了。
说了可能有点乱,不知道大家明白。
2,当age=30后,m1的hashcode确实改变了,但是遍历HashSet时,里面m1的hashcode跟改变属性后的一样,为什么不能删除呢?如果再设置age=27,却是可以删除的。
3,或者直接remvoe(new Man("Jimmy", 30)),这里的元素的hashcode、equals与m1都相等吧,可还是删不掉。
这个应该跟HashMap里的hashcode机制有关,请高手指点。