关于 Arraylist和HashSet中元素比较的问题

wenhuang1025 2016-05-29 10:44:03
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呢??

求能指点
...全文
561 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
sky_08_06_02 2016-08-19
  • 打赏
  • 举报
回复
引用 4 楼 m2200 的回复:
你分析一下ArrayList和HashSet的contains源代码你就知道了。 先看看ArrayList的,contains调用了indexOf,indexOf里面调用了equals方法,这个equals方法是关键,是直接从AbstractList里面继承来的,所以你要去这个类去查看equals源码,看了之后你就明白了,他是调用了你所添加对象的equals方法,而你重写了TestTest的equals方法,自然根据你重写的方法,得出了true。 再看看HashSet的,按顺序依次查看你会查看到getEntry这个方法里,重点是int hash = (key == null) ? 0 : hash(key);以及if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; 这两段代码,而((k = e.key) == key || (key != null && key.equals(k)))这部分根据你所实现的对象的equals方法,所以他是得出true的,所以e.hash == hash是不相等的,也就是你原先存入的对象的hash值和你新new出来的hash值不一样,也就是int hash = (key == null) ? 0 : hash(key);算出来的不一样。 至于二楼说的set是用==判断,个人觉得貌似好像不是很正确,比如下面的代码就可以推翻那个结论:
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
「已注销」 2016-07-26
  • 打赏
  • 举报
回复
需要充分理解 hashCode()和equals()的关系 如果2个对象equals返回true,那么hashCode()也必须相同 如果2个对象hashCode()返回相同值,equals()未必相同 重新equals必须重写hashCode,否则在进行hash相关操作时都会紊乱 因为hash相关操作时,会先比较hash再调用equals 如果不重新hashCode,就会导致equals返回true的2个对象,其hashCode不同
nikyotensai 2016-07-26
  • 打赏
  • 举报
回复
引用 4 楼 m2200 的回复:
你分析一下ArrayList和HashSet的contains源代码你就知道了。 先看看ArrayList的,contains调用了indexOf,indexOf里面调用了equals方法,这个equals方法是关键,是直接从AbstractList里面继承来的,所以你要去这个类去查看equals源码,看了之后你就明白了,他是调用了你所添加对象的equals方法,而你重写了TestTest的equals方法,自然根据你重写的方法,得出了true。 再看看HashSet的,按顺序依次查看你会查看到getEntry这个方法里,重点是int hash = (key == null) ? 0 : hash(key);以及if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; 这两段代码,而((k = e.key) == key || (key != null && key.equals(k)))这部分根据你所实现的对象的equals方法,所以他是得出true的,所以e.hash == hash是不相等的,也就是你原先存入的对象的hash值和你新new出来的hash值不一样,也就是int hash = (key == null) ? 0 : hash(key);算出来的不一样。 至于二楼说的set是用==判断,个人觉得貌似好像不是很正确,比如下面的代码就可以推翻那个结论:
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
惭愧惭愧
nikyotensai 2016-07-26
  • 打赏
  • 举报
回复
引用 2 楼 NewMoons 的回复:
楼上对set元素不重复理解的并不透彻,set内元素不重复指的是对变量的引用不重复,即判断的是元素指向的内存地址不重复。而是不指的值不重复(通过equals判断相等的对象为值相同,但并不代表他们引用的内存地址相同)。 把上面的代码改造下,看看输出就明白了。
List list = new ArrayList();
		Set set = new HashSet();
		TestTest test = new TestTest(1);
		list.add(test);
		list.add(test);
		set.add(test);
		set.add(test);
		
		System.out.println(list.size())//输出为2;
		System.out.println(set.size());//输出为1
了解了上面这点,就明白了这两个类方法contains的不同,List是根据对象equals方法来判断,而Set是根据==来判断。 楼主可以根据我的论述自己写些测试代码验证下。 至于java为什么这么设计,那就上升到数据结构和算法的问题了,请自行脑补,呵呵。
+1
NewMoons 2016-07-26
  • 打赏
  • 举报
回复
引用 6 楼 redduke1202 的回复:
需要充分理解 hashCode()和equals()的关系 如果2个对象equals返回true,那么hashCode()也必须相同 如果2个对象hashCode()返回相同值,equals()未必相同 重新equals必须重写hashCode,否则在进行hash相关操作时都会紊乱 因为hash相关操作时,会先比较hash再调用equals 如果不重新hashCode,就会导致equals返回true的2个对象,其hashCode不同
兄才所言极是,具体到楼主的问题,ArrayList判断对象是否包含只通过equals方法返回是否为真判断。而HashSet除了判断equals方法,还要同时判断hascode方法返回的值是否一样来决定返回真假。楼主的代码里自定义类只重写了equals而没有重写hashcode方法,而系统的hashcode方法默认是返回对象的引用内存地址(大概是这个值,可以这么理解),所以导致了他不理解的结果。 为什么HashSet要这么判断,这是源于Set接口在定义时就声明了实现类必须满足对象不能重复的约定。和List相比,除了无序外,这也是两者最大的区别。 追的再深点,hashcode有什么意义?简单地说是sun在设计时为了让包含在集合里的对象能有个索引,以提高集合类查找的效率。 当然,如果你没有这个需求,完全可以无视hashcode,但同时在调用api上也要小心了。
NewMoons 2016-07-25
  • 打赏
  • 举报
回复
4楼真知灼见,偶想当然了,惭愧。
爱睡觉的阿狸 2016-06-03
  • 打赏
  • 举报
回复
你分析一下ArrayList和HashSet的contains源代码你就知道了。 先看看ArrayList的,contains调用了indexOf,indexOf里面调用了equals方法,这个equals方法是关键,是直接从AbstractList里面继承来的,所以你要去这个类去查看equals源码,看了之后你就明白了,他是调用了你所添加对象的equals方法,而你重写了TestTest的equals方法,自然根据你重写的方法,得出了true。 再看看HashSet的,按顺序依次查看你会查看到getEntry这个方法里,重点是int hash = (key == null) ? 0 : hash(key);以及if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; 这两段代码,而((k = e.key) == key || (key != null && key.equals(k)))这部分根据你所实现的对象的equals方法,所以他是得出true的,所以e.hash == hash是不相等的,也就是你原先存入的对象的hash值和你新new出来的hash值不一样,也就是int hash = (key == null) ? 0 : hash(key);算出来的不一样。 至于二楼说的set是用==判断,个人觉得貌似好像不是很正确,比如下面的代码就可以推翻那个结论:
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
键圣 2016-06-02
  • 打赏
  • 举报
回复
我想问一下你为什么不重写hashCode()? 两个Object 对象的hashCode()默认返回的值是不一样的 看过的书上都会提到吧,不重写的时候在集合里面会出现混乱的,HashSet其实内部是使用HashMap<E,Object>实现的 ArrayList的内部实现是动态数组
东溪陈姓少年 2016-05-30
  • 打赏
  • 举报
回复
每次newtesttest(1)的时候都是new了一个全新的对象 set的元素不能重复啊 当然返回false
NewMoons 2016-05-30
  • 打赏
  • 举报
回复
楼上对set元素不重复理解的并不透彻,set内元素不重复指的是对变量的引用不重复,即判断的是元素指向的内存地址不重复。而是不指的值不重复(通过equals判断相等的对象为值相同,但并不代表他们引用的内存地址相同)。 把上面的代码改造下,看看输出就明白了。
List list = new ArrayList();
		Set set = new HashSet();
		TestTest test = new TestTest(1);
		list.add(test);
		list.add(test);
		set.add(test);
		set.add(test);
		
		System.out.println(list.size())//输出为2;
		System.out.println(set.size());//输出为1
了解了上面这点,就明白了这两个类方法contains的不同,List是根据对象equals方法来判断,而Set是根据==来判断。 楼主可以根据我的论述自己写些测试代码验证下。 至于java为什么这么设计,那就上升到数据结构和算法的问题了,请自行脑补,呵呵。

62,628

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧