黑马程序员技术交流社区

标题: HashSet存储重复元素的问题 [打印本页]

作者: 陈冲    时间: 2012-9-15 21:29
标题: HashSet存储重复元素的问题
本帖最后由 陈冲 于 2012-9-15 22:03 编辑

今天复习张孝祥老师的视频,在看到反射那部分的时候,有个HashSet的应用,在修改了对象的值以后,该元素无法移除。
我就在想是否能添加一个对象,这个对象的值与上面修改后的对象的值相同,没想到真的成功了。
有没有知道这是为什么的?
以下是代码:
  1. public class ReflectPoint {
  2.         private Date birthday=new Date();
  3.         private int x;
  4.         public int y;
  5.         
  6.         public ReflectPoint(int x, int y) {
  7.                 super();
  8.                 this.x = x;
  9.                 this.y = y;
  10.         }

  11.         @Override
  12.         public String toString() {
  13.                 // TODO Auto-generated method stub
  14.                 return "x:"+x+" y:"+y;
  15.         }

  16.         @Override
  17.         public int hashCode() {
  18.                 final int prime = 31;
  19.                 int result = 1;
  20.                 result = prime * result + x;
  21.                 result = prime * result + y;
  22.                 return result;
  23.         }

  24.         @Override
  25.         public boolean equals(Object obj) {
  26.                 if (this == obj)
  27.                         return true;
  28.                 if (obj == null)
  29.                         return false;
  30.                 if (getClass() != obj.getClass())
  31.                         return false;
  32.                 ReflectPoint other = (ReflectPoint) obj;
  33.                 if (x != other.x)
  34.                         return false;
  35.                 if (y != other.y)
  36.                         return false;
  37.                 return true;
  38.         }
  39. }
复制代码
  1. import java.util.Collection;
  2. import java.util.HashSet;

  3. public class ReflectTest {
  4.         public static void main(String[] args) {
  5.                 Collection collections=new HashSet();
  6.                 ReflectPoint pt1=new ReflectPoint(3, 3);
  7.                 ReflectPoint pt2=new ReflectPoint(5, 5);
  8.                 ReflectPoint pt3=new ReflectPoint(3, 3);
  9.                 collections.add(pt1);
  10.                 collections.add(pt2);
  11.                 collections.add(pt3);
  12.                 collections.add(pt1);
  13.                
  14.                 System.out.println(collections.size());
  15.                 System.out.println(collections);
  16.                
  17.                 pt1.y=7;
  18.                 collections.remove(pt1);
  19.                
  20.                 collections.add(new ReflectPoint(3,7));//上面的pt1修改为了x=3,y=7,这里再次添加3,7
  21.                
  22.                 System.out.println(collections.size());
  23.                 System.out.println(collections);
  24.         }
  25. }
复制代码
输出结果是 [x:3 y:7, x:3 y:7, x:5 y:5]
作者: 张 涛    时间: 2012-9-15 21:45
  比如:
你new了一个某类型的对象c,内容是x-3,y-6;
然后你把这个对象c添加到了map中,map中有一个某类型的对象,内容是x-3,y-6。
然后你修改了c,变为了x-4,y-6;
然后你删c,在map中找不到c,因为找的时候,是按照equals方法和hashmap方法比较的。第一次添加的某类型对象的内容是x-3,y-6,和现在c对象的x-4,y-6不匹配。所以找不到。
然后你添加一个x-4,y-6,这个按照equals方法和hashcode方法,发现在map中没有和他相同的,当然可以添加了。
作者: 佟亚鹏    时间: 2012-9-15 21:52
呵呵,张老师的高新技术视频我看三遍了,每次都有新的收获。关于这个,请看代码中我加的注释
  1. import java.util.Collection;
  2. import java.util.HashSet;

  3. public class ReflectTest {
  4.         public static void main(String[] args) {
  5.                 Collection collections=new HashSet();
  6.                 ReflectPoint pt1=new ReflectPoint(3, 3);
  7.                 ReflectPoint pt2=new ReflectPoint(5, 5);
  8.                 ReflectPoint pt3=new ReflectPoint(3, 3);//这个加不进去了,
  9. <span style="color: rgb(51, 102, 153); font-family: Monaco, Consolas, 'Lucida Console', 'Courier New', serif; font-size: 12px; line-height: 21.600000381469727px; background-color: rgb(255, 255, 255); ">ReflectPoint </span> 的hashcode是根据,x、y算出来的
  10.                 collections.add(pt1);
  11.                 collections.add(pt2);
  12.                 collections.add(pt3);//加不进去,重复了
  13.                 collections.add(pt1);//加不进去,重复了
  14.                
  15.                 System.out.println(collections.size());//结果是2
  16.                 System.out.println(collections);
  17. //目前集合中的元素是 <span style="background-color: rgb(255, 255, 255); "> [x:3 y:3, x:5 y:5]</span>  
  18.                
  19.                 pt1.y=7;//修改了pt1的y值,hashcode变掉了,由于移除对象需要判断它的hashcode,此时集合中不存在这个被修改的hashcode值,集合保存的hashcode仍然是修改前的hashcode,所以这个对象移除不了
  20.                 collections.remove(pt1);
  21. //虽然集合中已经有了个x=3,y=7的pt1对象,但是集合中它所在的hash区域,仍然是根据x=3,y=3时计算出的hashcode值确定的,而加入的对象的hashcode值是根据x=3,y=7计算出来的,所以在集合中与pt1的hashcode值不同,则认为这个对象在集合中不存在所以存了进来
复制代码
过程都写在了注释中了,楼主好好体会下,不是看x、y值,重点看hashcode的值,对象放在集合中的位置是根据加入集合时的hashcode值确定的,加入新的对象时,根据hashcode值判断下相应区域是否有这个对象,明白这些,就能理解了

作者: 程振    时间: 2012-9-15 22:01
这个问题你需要查看JDK的源码
首先看HashSet的Add方法,因为HashSet是利用现有的HashMap来实现的,即把HaspMap的value固定为PRESENT 对象:private static final Object PRESENT = new Object();

这个你要理解

然后你调用HashSet的add方法时,执行下面的代码
  1. public boolean add(E e) {
  2. return map.put(e, PRESENT)==null;  //调用HashMap的put方法
  3. }
复制代码
下面需要看看HashMap的put方法
看里面的关键两句
  1. public V put(K key, V value) {
  2. if (key == null)
  3. return putForNullKey(value);
  4. int hash = hash(key.hashCode());             //
  5. int i = indexFor(hash, table.length);          //  确定key在hash表中的位置
  6. for (Entry<K,V> e = table[i]; e != null; e = e.next) {
  7. Object k;
  8. if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
  9. V oldValue = e.value;
  10. e.value = value;
  11. e.recordAccess(this);
  12. return oldValue;
  13. }
  14. }

  15. modCount++;
  16. addEntry(hash, key, value, i);
  17. return null;
  18. }
复制代码
所以当你调用HashSet的add方法时,value的位置已经确定了,之后你修改的对象对HashSet也产生影响,因为它们都指向同一个内存地址。

简单的说就是插入时根据value的hashcode,看看对应的表的位置有没有占用,以此来判断是否添加成功。







欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2