黑马程序员技术交流社区
标题: 关于HashSet集合引起内存泄露的问题 [打印本页]
作者: 刘士林 时间: 2012-10-30 11:11
标题: 关于HashSet集合引起内存泄露的问题
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
HashSet hs=new HashSet();
Person p1=new Person("lili01",18);
Person p2=new Person("lili02",14);
hs.add(p1);
hs.add(p2);
System.out.println(hs.size());
p1.age =16;
hs.remove(p1);
System.out.println(hs.size());
}
}
class Person
{
private String name;
int age;
Person(String name, int age)
{
this.name=name;
this.age =age;
}
public int hashCode()
{
return name.hashCode()+age*17;
}
}
打印结果如下:
2
2
删除失败。
往集合中存入了两个元素,而将p1的age重新赋值后,就删除不了p1了,集合中仍有p1存在。时间长了内存泄露。
根据毕老师的讲解,集合中存入的不是对象本身,只是对象的引用。那照此看来,集合中仍有p1 的引用存在。
那么p1 的引用指向谁呢,是lili01,18还是lili01,16呢?是不是应该只有一个lili01 对象呢?
删除元素时是拿着引用找到堆内存中对象,再调用对象的hashCode算法算出哈希值,在和集合中的哈希值比较吗?
作者: 小灰灰 时间: 2012-10-30 11:20
我认为,删除失败的原因 是重写了 hashCode() 方法,而不是,将p1的age重新赋值后,就不能删除了
作者: 林志进 时间: 2012-10-30 11:58
HashSet hs = new HashSet();
Person p1 = new Person("lili01", 18);
System.out.println("p1==" + p1.hashCode() + "age=" + p1.age);// 打印源p1的值
// hs存进来的就是该数值
Person p2 = new Person("lili02", 20);
hs.add(p1);
hs.add(p2);
p1.age = 225;
System.out.println("p1==" + p1.hashCode() + "age=" + p1.age);// 打印属性值改变后的的p1
System.out.println(hs.remove(p1));// 返回false,删除失败。因为hs是用p1现在hashcode值来在集合中遍历,自然无法删除(集合中的hashcode没改变)
for (Iterator it = hs.iterator(); it.hasNext();) {
Person p = (Person) it.next();
System.out.println("px==" + p.hashCode() + "=====age=" + p.age);// 而集合中的对象实际上是还是p1,只是属性值和hashcode都改变过了
}
System.out.println(hs.size());
System.out.println("重写的hashcode方法最好写的复杂点");
作者: 张忠豹 时间: 2012-10-30 12:06
当一个对象被存储进HashSet集合中以后,就不能修改这个对象中那些参与计算哈希值的字段了,否则,对象修改以后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使contains方法使用该对象的当前引用作为参数去HashSet集合中检索对象,也将返回找不到对象的结果(字段值一改变,hashcode就会改变,相应的引用地址也会改变,导致以前的引用作废),这也会导致无法从HashSet集合中单独删除当前对象,从而造成内存泄露。
package staticImport.cn.itcast.day1;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashSet;
import java.util.Properties;
public class ReflectTest2 {
public static void main(String args[])throws Exception{
/**已经知道集合类型*/
Collection<ReflectPoint> collections = new HashSet<ReflectPoint>();
ReflectPoint pt1 = new ReflectPoint(3,3);
ReflectPoint pt2 = new ReflectPoint(5,3);
ReflectPoint pt3 = new ReflectPoint(3,3);
collections.add(pt1);
collections.add(pt2);
collections.add(pt3);
collections.add(pt1);
/*1、当重写了ReflectPoint中的hashCode与equals方法之后pt4就存不到collections中了
这时候,size的值为2*/
/*某一个对象存入到集合中之后,参与hashCode运算的属性就不要改动了,改完之后就会造成内存泄露。如果不断的在
* 集合中添加对象,修改对象,删除对象,以此循环,最终的结果就是内存溢出*/
pt1.y = 9;//修改属性y导致pt1的hashCode改变,以前的引用变得没有作用,导致remove不掉
System.out.println(collections.remove(pt1));//false
System.out.println(collections.size());
}
}
class ReflectPoint{
private int x;
public int y;
public String str1 = "basketball";
protected String str2 = "aaaaaaaaaaCCCCCCCC";
public String str3 = "ababababa";
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public String toString(){
return str1+" "+str2+" "+str3;
}
//在ReflectTest2中要使用
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ReflectPoint other = (ReflectPoint) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
public String getStr1() {
return str1;
}
public void setStr1(String str1) {
this.str1 = str1;
}
}
作者: 李兆宁 时间: 2012-10-30 12:09
那两个这个对象就没有引用了?
作者: 黑马张旭明 时间: 2012-10-30 12:29
那么p1 的引用指向谁呢,是lili01,18还是lili01,16呢?是不是应该只有一个lili01 对象呢?
在这个例子中,p1的引用从开始到结束都没有改变,始终指向内存中的那个对象,p1在HashSet中的位置也没有改变,始终在最初HashCode()值所对应的存储地方。
那什么改变了?
HashSet寻找pt1的方法改变了,pt1的age属性改变后,导致HashSet寻找pt1所计算出来的HashCode与最初的HashCode不一样,也就是没有找到正确的存储空间,导致了pt1删除不掉。pt1依旧存在它最开始所在的区域,只是HashSet找错了。
删除元素时是拿着引用找到堆内存中对象,再调用对象的hashCode算法算出哈希值,在和集合中的哈希值比较吗?
是这样的,删除和查找都需要哈希值,换句话说只要是哈希值一样,并且equals方法返回true,就算传入的引用不是指向pt1也能删除HashSet集合中的pt1。
作者: 刘士林 时间: 2012-10-30 12:52
黑马张旭明 发表于 2012-10-30 12:29
那么p1 的引用指向谁呢,是lili01,18还是lili01,16呢?是不是应该只有一个lili01 对象呢?
在这个例 ...
谢谢你的回答,很简明!
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) |
黑马程序员IT技术论坛 X3.2 |