您的位置:首页 > 其它

关于 Arraylist和HashSet中元素比较的问题所引发的思考

2016-07-26 14:42 351 查看
问题源自我在论坛回复网友的一个帖子,名字就是【关于 Arraylist和HashSet中元素比较的问题】,问题如下:
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class TestTest {
int value;
TestTest(int value) {
this.value = value;
}
public boolean equals(Object obj) {
if (obj instanceof TestTest) {
TestTest foo = (TestTest) obj;
return this.value == foo.value;
} else {
return false;
}
}
public static void main(String[] args) {
List list = new ArrayList();
Set set = new HashSet();
list.add(new TestTest(1));
set.add(new TestTest(1));
System.out.println(list.contains(new TestTest(1)));   //结果一:true
System.out.println(set.contains(new TestTest(1)));  //结果二 :false
System.out.println((new TestTest(1)).equals(new TestTest(1)));
}
}


为什么结果一为TRUE,而结果二位FALSE呢??

这个问题其实只要搞明白了java不同集合判断元素重复的算法即可。我回答时想当然的认为:HashSet内元素不重复指的是对变量的引用不重复,即判断的是元素指向的内存地址不重复。

这句话貌似很正确,大部分情况下似乎没有问题。而一个网友给出的一个反例让我意识到我是大错特错了。

请运行如下代码,
HashSet<Object> hs = new HashSet<>();

String s1 = "sss";
String s2 = new String("sss");
System.out.println(s1 == s2);//false

hs.add(s1);
System.out.println(hs.contains(s2));//true


结果已经很清楚的证明我的结论是错误的,那么真相到底是什么?

还是先看看java源代码吧,貌似这是解决问题最有说服力的方法。这里就略了,简单的结论就是:

ArrayList判断对象是否包含只通过equals方法返回是否为真判断。

HashSet除了判断equals方法,还要同时判断hascode方法返回的值是否一样来决定返回真假。

楼主的代码里自定义类只重写了equals而没有重写hashcode方法,而系统的hashcode方法默认是返回对象的引用内存地址(大概是这个值,可以这么理解),所以导致了他不理解的结果。

我们可以再引深一点,为什么还要判断hashcode值,它有什么意义?

为什么HashSet要这么判断,这是源于Set接口在定义时就声明了实现类必须满足对象不能重复的约定。和List相比,除了无序外,这也是两者最大的区别。

那hashcode有什么意义?简单地说是sun在设计时为了让包含在集合里的对象能有个索引,以提高集合类查找的效率。

一个经典的论述:

1、如果两个对象通过equals比较为真,那么他们的hashCode返回的值也应该相等。两个值相等的对象其hashcode值也必须是相等的。

2、两个对象的hashCode返回的值相等,通过equals比较未必就是真。也就是说hashcode值相等的两个对象其值未必是相等的。

结论:

为了保证你的自定义类在做集合类操作时不出错误,请保证以下一点:

如果你重写了equals方法,那同时最好也要重写hashCode方法,而且必须保证当a.equals(b)=true时,a和b的hashCode返回的值是相同的。

java是一个强类型语言,所以它很健壮,sun在设计这个语言时就已经替我们考虑了很多东西。

当然,上面的论述只是一个约定,如果你没有hashcode这个需求,完全可以无视,但同时在调用api上也要小心了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: