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理解有问题

解决方案 »

  1.   

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

  2.   

    回一楼。我修改如下:
     m1.age = 30; 
     m2.age = 30; 
     m3.age = 30; 
     
    再调用
    hs.remove(m1); 为什么仍然一个也没有删除呢?
      

  3.   

    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值进行对比,发现不一致导致无法删除吧,我只是猜测这样...
      

  4.   

    回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 不再包含该元素)。 如果按照这个规则,应该能够删除的。
      

  5.   

    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。
      

  6.   

    肯定不成功啊....
    你修改了一个age,那么所有的hashCode和添加前进之前都不相同吧?你怎么可能找到对象呢?
    前是Jimmy+27,28,29
    移除的时候是Jimmy+30,与任何一个都不相同,怎么能移除成功呢?
      

  7.   

    移除之前是一个找的过程,怎么找?就是通过hash(hasCode)来找.每个的hashCode都不相同,所以找不到对象,也就是移除不成功.
      

  8.   

    按照我的理解:
    第一次执行men.add(m1)时,假设JVM计算出m1的哈希值为C1,然后根据哈希值C1把m1存放到位置P1;
    执行men.remove(m1)时,m1的age被改为30,JVM计算出m1的哈希值为C2,根据此哈希值,JVM会到位置P2去找m1,但是m1真正的存放位置是P1,因此无法找到m1,故删除失败
      

  9.   

    添加时先比较hashCode,没有相等的,直接加入,有相等的,进入此相等队列, 再看是否连等,有连等舍弃,没有连等 最后看euqals(),有true 舍弃,没有加入。
    删除时顺序一样。。
    Man类要重载hashCode() equals()方法
    09年的帖子拿出来忽悠人
      

  10.   

    这么简单的东西就不要拿出现问显了,看看HashSet中remove的源码。
    源码当中有先是对hashCode进行了比较,然后又进行了equals比较。
    而你这个程序当中,首先HasheSet存了Man m1 = new Man("Jimmy", 27);
    age = 27,然后age = 30,测试了一个下这里会产生使得m1的前后两个hashCode码不相同。
    把hashCode方法改为:
    public int hashCode() {
         return name.hashCode();
    }
    即可,我已经测试,贴主可以试试。
      

  11.   

    前面回答的有点模糊,很多人会问为什么前后的两个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不了。
    说了可能有点乱,不知道大家明白。
      

  12.   

    1,不修改跟hashcode有关的属性,操作是没有问题的。
    2,当age=30后,m1的hashcode确实改变了,但是遍历HashSet时,里面m1的hashcode跟改变属性后的一样,为什么不能删除呢?如果再设置age=27,却是可以删除的。
    3,或者直接remvoe(new Man("Jimmy", 30)),这里的元素的hashcode、equals与m1都相等吧,可还是删不掉。
    这个应该跟HashMap里的hashcode机制有关,请高手指点。