您的位置:首页 > 职场人生

黑马程序员——ArrayList&HashSet&Hashcode的学习总结

2014-04-24 10:53 495 查看
直观上来说,ArrayList不判断集合内的元素的重复性,HashSet会保持集合元素的唯一性,即如果有重复的元素,则后一个重复的元素就不会加入到HashSet集合里面,如下所示:

public static void main(String[] args) {
HashSet arr = new HashSet();
// ArrayList arr = new ArrayList();
Test33 test1 = new Test33(1, 1);
Test33 test2 = new Test33(1, 1);
Test33 test3 = new Test33(1, 2);
arr.add(test1);
arr.add(test2);
arr.add(test3);
arr.add(test1);
System.out.println(arr.size());
}
}

class Test33 {
int x;
int y;

public Test33(int x, int y) {
super();
this.x = x;
this.y = y;
}

}


如果用ArrayList集合来存放元素,最后的输出结果为4,如果用HashSet,输出结果为3。

这是因为在HashSet中,test1已经存在了,如果再用add()方法加入一个test1,则这个test1为重复对象,不会加入到集合中。

那HashSet是通过什么方法判断元素是否重复呢?是通过equals()方法。在没有重写equals()方法前,==和equeals()这两个比较的方法是一样的,都是比较内存的引用地址,如果引用地址都一样,则两个元素相同,对于HashSet集合来说,如果两个元素的equals()为true,则后一个元素不会被加入到集合中。当然,如果是一个一个的和集合中已有的元素进行比较,那效率就太低了,所以这里就用到了Hashcode,如下图所示:

HashSet把集合分成了不同的区域,每个区域内存放的是拥有相似hashcode的元素,如果有新元素需要加入集合,则只要在新元素对应的hashcode区域内找有没有重复元素即可,查找的效率自然就提高了。

如果想把值相同但引用不同的对象也当成是同一对象来看待的话,可以重写equals()方法,在eclipse中通过快捷生成方式覆盖equals()方法:

public static void main(String[] args) {
HashSet arr = new HashSet();
// ArrayList arr = new ArrayList();
Test33 test1 = new Test33(1, 1);
Test33 test2 = new Test33(1, 1);
Test33 test3 = new Test33(1, 2);
arr.add(test1);
arr.add(test2);
arr.add(test3);
arr.add(test1);
System.out.println(arr.size());
}
}

class Test33 {
int x;
int y;

public Test33(int x, int y) {
super();
this.x = x;
this.y = y;
}

@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;
Test33 other = (Test33) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}

}


这样输出的结果就为2了,因为重写了equals()方法,test1和test2的x,y值相同,所以就被当成是同一元素,test2便不会加入到HashSet集合中。

这里还需要注意两个问题:

1:==和equals()的区别

上面已经说了,对应没有重写equals()的类,==和equals()比较的都是指向对象的内存引用地址,两者比较的结果是一样的。而String类默认重写了这个方法,用equals()比较两个字符串时是比较字符串的内容而非引用地址,所以,对于如下代码:

String s1 = new String("123");

String s2 = new String("123");


如果用==比较的话结果会是false,用equals()比较的话结果是true。但特别的:

String s1 = "123";

String s2 = "123";


这个无论是==还是equals()结果都是true,因为s1与s2分别指向由字符串常量”123” 创建的对象,在常量池中,只有一个对象,内容为123,有两个引用s1和s2指向这个对象,故这两个引用变量所指向的地址是相同的,所以无论是值比较还是引用比较都是true。

2:JAVA的内存泄漏问题

工作中接触到这个问题的时候会有一个疑问——JAVA有GC自动垃圾回收机制,不用的对象会自动销毁,内存空间也会被释放,那么还会出现内存泄漏的问题么?

答案是会的,而且正因为有了GC,所以JAVA的内存泄漏变得更隐蔽更难发现,连GC都不能回收这些引起泄漏的内存地址,程序员来找不就更麻烦了么?刚好对于HashSet这个集合,如果操作不当就会引起内存泄漏。

public static void main(String[] args) {
HashSet arr = new HashSet();
// ArrayList arr = new ArrayList();
Test33 test1 = new Test33(1, 1);
Test33 test2 = new Test33(2, 2);
Test33 test3 = new Test33(3, 3);
arr.add(test1);
arr.add(test2);
arr.add(test3);
test1.x=2;//改变了test1中的x的值
arr.remove(test1);//集合中删除test1
System.out.println(arr.size());
}
}

class Test33 {
int x;
int y;

public Test33(int x, int y) {
super();
this.x = x;
this.y = y;
}

@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;
Test33 other = (Test33) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}

}

如上所示,重写了equals()方法,让其比较的是两个对象中x和y的数值而非引用地址,然后创建了三个不同的实例对象,x,y的值都不一样,加入到HashSet中,这时集合中有三个元素,size()的结果为3,然后把其中一个对象的x值改变,再在HashSet中把这个对象移除remove()掉,按理说这时size()的值应该为2,但实际运行结果为3,说明test1这个对象没有被删除,为什么呢?

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}

根据类中重写的这个方法,hashcode的值用到了x和y的值,而x的值一旦改变了,hashcode的值肯定也改变,根据上面所说的,HashSet集合是按照不同的hashcode值分成不同的区域来存放元素,当一个元素的hashcode值改变后再移除这个元素,HashSet集合就会在这个元素的新的hashcode值所对应的区域中寻找,当然找不到这个元素,结果也就移除不了了。但这个元素还是实实在在的存在在HashSet集合中,存在在内存中,已经用不着了但又删除不掉,这边会造成内存的泄漏,而改变集合中某个元素的值的操作又是何其普遍,如果真的出现问题,GC机制肯定是帮不上忙,程序员要找问题所在也会陷入巨大的麻烦中。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐