A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 顾传文 中级黑马   /  2013-2-21 08:47  /  2225 人查看  /  9 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 顾传文 于 2013-3-12 09:17 编辑

import java.util.HashSet;
import java.util.Set;
public class Test {
public static void main(String[] args) throws Exception{
  Set set = new HashSet();
  Point p1 = new Point("lisi","nanjing");
  Point p2 = new Point("wangwu","shanghai");
  Point p3 = new Point("zhaoliu","beijing");
  set.add(p1);
  set.add(p2);
  set.add(p3);
  System.out.println(set.size());
  p1.name = "zhangsan";
  set.remove(p1);
  System.out.println(set.size());
}
}
class Point{
public String name;
public String address;
public Point(String name,String address){
  this.name = name;
  this.address = address;
}
public int hashCode() {
  return name.hashCode()*address.hashCode();
}
}
我往HashSet集合中添加3个元素,后来想减少一个,为什么还是3个元素呢?

评分

参与人数 1技术分 +1 收起 理由
Rancho_Gump + 1

查看全部评分

9 个回复

倒序浏览
本帖最后由 柴乔军 于 2013-2-21 10:07 编辑

2楼的方法可以解决这个问题,但原因不是因为new出来的对象,是因为你修改了其中的属性, p1.name = "zhangsan";

hashcode值是根据这些属性计算出来的,当你修改以后,他在内存中的地址值就会发生变化,引用地址发生了变化

所以当你删除时,已然找不到这个对象,所以删除操作就会返回false,删除失败了
  1. System.out.println(set.size());
  2. System.out.println(p1);
  3. p1.name = "zhangsan";
  4. System.out.println(p1);
复制代码
3
day3.Point@2fad6729
day3.Point@63e7f79c
false
3

评分

参与人数 1黑马币 +9 收起 理由
张晋瑜 + 9 赞一个!

查看全部评分

回复 使用道具 举报
看看你的覆盖hashcode,
public int hashCode() {
  return name.hashCode()*address.hashCode();
}
你是用name和addres的hashcode来编码对象Point的hashcode,所以你只要改变对象name和address任意一个值都会无法删除对象,
这就是所谓的内存泄露
所以当你想复写hashcode和equals可以限定同一个Point时最好把name和address改为private权限,可以避免以上现象

评分

参与人数 1黑马币 +9 收起 理由
张晋瑜 + 9 赞一个!

查看全部评分

回复 使用道具 举报
你已经写入hashset集合了,然后又修改了其元素中的字段,
所以hashcode的值就会改变,
当你删除时,就会找这个改变了值的元素,但是原来的元素还在,在张孝祥高级视频中,
说到这个问题,叫做内存泄露,
发生在使用到hashcode的集合中

评分

参与人数 1技术分 +1 收起 理由
李培根 + 1 赞一个!

查看全部评分

回复 使用道具 举报
本帖最后由 铿锵科技 于 2013-2-21 11:01 编辑

Set set = new HashSet();
  Point p1 = new Point("lisi","nanjing");
  Point p2 = new Point("wangwu","shanghai");
  Point p3 = new Point("zhaoliu","beijing");
  set.add(p1);
  set.add(p2);
  set.add(p3);
  System.out.println(set.size());//集合中已存入3个Point对象
  p1.name = "zhangsan";//你将集合外的Point对象p1的name属性改为"zhangsan"
  set.remove(p1);//你要删除集合中和p1值一样的元素,但集合中并不存在name="zhangsan"这样的元素(存在name="lisi"),所以是无法删除的
  System.out.println(set.size());//上面没删除集合中任一值,所以这集合还存有3个值

评分

参与人数 1技术分 +1 收起 理由
李培根 + 1 赞一个!

查看全部评分

回复 使用道具 举报
我仔细的调试了一下这个小程序。
首先,2楼的回答是错误的,就算再重写一个equals()方法也没用,因为楼主所写的Point类的hashCode()方法的缘故已经找不到p1了(确实会内存泄露,这只是后果)。为什么找不到,因为根据楼主的hashCode()方法计算hash码是与属性相关的。
其次,我觉得3、4楼的答案更正确
最后,对于这个小程序,我遇到了另外一个问题,想向大家请教:
对这个小程序做个小修改,就是Point中equals()、hashCode()都不重写

import java.util.HashSet;
import java.util.Set;
public class Test {
public static void main(String[] args) throws Exception{
  Set set = new HashSet();
  Point p1 = new Point("lisi","nanjing");
  Point p2 = new Point("wangwu","shanghai");
  Point p3 = new Point("zhaoliu","beijing");
  set.add(p1);
  set.add(p2);
  set.add(p3);
  System.out.println(set.size());
  p1.name = "zhangsan";
  set.remove(p1);
  System.out.println(set.size());
}
}
class Point{
public String name;
public String address;
public Point(String name,String address){
  this.name = name;
  this.address = address;
}
}
我发现结果是正确的,大家有什么看法说说吧,当能重写这两个方法是有必要的,大家说说看应该怎么重写?

评分

参与人数 1技术分 +1 收起 理由
李培根 + 1 赞一个!

查看全部评分

回复 使用道具 举报
本帖最后由 唐长智 于 2013-2-23 01:09 编辑

楼主纠结的是HashSet的remove()方法和hashcode(),equals()之间的关系。
那么就要从HashSet各个方法的底层调用说起了。
先说remove();
HashSet的remove()方法的底层调用的是map集合的remove()方法。
//这是HashSet remove()的源码
public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }
map集合的remove方法则是
如果此映射包含从满足 (key==null ? k==null :key.equals(k)) 的键 k 到值 v 的映射关系,则移除该映射关系。
V remove(Object key);


可以看到,元素删除与否,与内存中的地址就是hashcode(),equals()方法有关。
HashSet集合从AbstractSet集合继承了hashcode()和equals()方法。
这两个方法肯定不会和集合元素的属性相关。
所以只要覆写这两个方法,让这两个方法的计算与集合元素的属性相关联,那么在更改属性之后,hashcode()的结果改变,equals()方法的结果改变。remove()方法就
不能删掉原来那个元素了。
假如不覆写这两个方法,那么无论如何更改元素属性,在1楼的实例中,remove()方法都能成功删掉元素。

评分

参与人数 1黑马币 +9 收起 理由
李培根 + 9 赞一个!

查看全部评分

回复 使用道具 举报
王钊 中级黑马 2013-2-23 12:03:19
8#
首先,HastSet集合判断两个元素相等的标准是两个对象通过equals9)方法比较相等,并且两个对象的hashCode()方法返回值也相等。
你这里在删除p1前,修改了p1的属性,而p1的hashCode计算方式,包括了p1.name属性
因而在remove时p1的hashCode已经与原来的p1不一样了,比较结果为false,HastSet不认为这两个是相同的元素,
所以删不掉。

评分

参与人数 1黑马币 +9 收起 理由
李培根 + 9 赞一个!

查看全部评分

回复 使用道具 举报
老张是视频里边说过了,是黎活明老师提出来的:参与hashCode运算的属性,在加入HashSet集合后,不能改变,否则会内存泄露。因为属性改变后,它的hashCode值也跟着改变,删除的时候,你拿这个修改后的哈希值永远也找不到已经存入的哈希值。
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马