关于 Arraylist和HashSet中元素比较的问题所引发的思考
2016-07-26 14:42
351 查看
问题源自我在论坛回复网友的一个帖子,名字就是【关于 Arraylist和HashSet中元素比较的问题】,问题如下:
为什么结果一为TRUE,而结果二位FALSE呢??
这个问题其实只要搞明白了java不同集合判断元素重复的算法即可。我回答时想当然的认为:HashSet内元素不重复指的是对变量的引用不重复,即判断的是元素指向的内存地址不重复。
这句话貌似很正确,大部分情况下似乎没有问题。而一个网友给出的一个反例让我意识到我是大错特错了。
请运行如下代码,
结果已经很清楚的证明我的结论是错误的,那么真相到底是什么?
还是先看看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上也要小心了。
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上也要小心了。
相关文章推荐
- 杭电-2546 饭卡(01背包)
- Servlet监听器,统计网站在线人数实例
- coding git 初始化与项目远程连接(二)
- ORACLE 10g AWR报告设置总结
- WPF中DataGrid 添加checkbox实现全选、多选
- codeforces 495B - Modular Equations
- dota回忆录
- angularJS在directive的template中使用ng-template
- Swift基础(二十三)UIImageView
- Odd Even Linked List
- 查找电脑上某个端口占用,并将其删除
- 第四章 字典
- 我的Android进阶之旅------>解决Error: specified for property 'mergedManifest' does not exist.
- 【数据结构】线性表的顺序存储完整程序
- Android常见异常总结
- java多线程学习
- 第一个博文
- unity GearVr环境搭建与打包
- Azure Table storage 基本用法
- 【运维工具】logrotate 日志管理神器