Java探究HashSet中如何完成同一个对象不重复出现

Java探究HashSet中如何完成同一个对象不重复出现(自定义对象作为HashSet的元素时候应该注意重写的hashcode与equals讨论)

2021.10.29 写于山威

1.继承体系

没有学过UML,自己瞎画的
Java探究HashSet中如何完成同一个对象不重复出现

2.问题讨论

我们都知道,在java的set中是不能出现重复的元素的,但是是如何保证这一问题的实现的呢?我们先买个关子,我们先想想,平时我们如何判断元素是否相等:

  • 对于基本数据类型:直接使用等于号
  • 对于引用对象:使用equals判断,当然也不排除string类这种重写hashcode等,使用hashcode来判断对象是否相等

所以我们一般都会理所应当的想到,在set中要满足插入元素没有重复的时候,一般是使用集合元素的equals方法来判断的,那么我们下面来写一段代码检测一下

//只是重写equals方法
	public static class A
	{

		@Override
		public boolean equals(Object obj) {
			return true;
		}
		
	}
	//只是重写hashcode方法
	public static class B
	{

		

		@Override
		public int hashCode() {
			return 1;
		}
		
		
	}
	//将equals方法和hashcode方法都重写
	public static class C
	{
		public boolean equals(Object obj) {
			return true;
		}

		@Override
		public int hashCode() {
			return 2;
		}
	}
	public static void main( String [] __)
	{
		//我们写了三个私有类,接下来我们测试一下
		HashSet<Object> test=new HashSet<Object>();
		test.add(new A());
		test.add(new A());
		test.add(new B());
		test.add(new B());
		test.add(new C());
		test.add(new C());
		//我们重复的加进去了六个元素,那么按照我们重写之后的方法(无论是什么都返回true与相同的hashcode)
		//这个set里面目前还有谁在保留呢?
		System.out.println(test);
	}

在这种情况下,大家猜测会输出什么呢?

[mytestDemo.Hello$B@1, mytestDemo.Hello$B@1, mytestDemo.Hello$C@2, mytestDemo.Hello$A@7de26db8, mytestDemo.Hello$A@1175e2db]
这是我的输出,可以很明显的看到,输出里面有两个A,两个B,但是只有一个C,原因其实很简单,因为重写方法的时候,我们只有对于C既重写hashcode,又重写了equals

3.源码解释

因此我们可以得到,set判断元素是否重复的时候,既使用了hashcode,又使用equals方法,具体是怎么使用,让我们进入源码看看(稍稍看看,深入看不能我能窥探的)
Java探究HashSet中如何完成同一个对象不重复出现

我们可以看到,在hashset中是有一个hashmap来保证插入的元素是不会重复的(这一点我也没有想到)

Java探究HashSet中如何完成同一个对象不重复出现

Java探究HashSet中如何完成同一个对象不重复出现

具体这里的put方法中有一个参数是hash(value),我们可以看到在这个函数里面其实就是调用了对象的hashcode函数,用得到的这个函数去检索set集合中相应位置是否有元素(set说是无序,其实内部是使用hashcode作为序的),因此会有下面的几种情况

  • 重写的hashcode计算得到的数据对应位置没有其他元素,插入进去(最好的情况)
  • 没有重写hashcode方法,使用object的hashcode方法返回地址值作为索引,因为地址值唯一,所以一定可以插入(类似于刚刚类A)
  • 重写了hashcode方法,但是没有重写equals方法,类似于刚刚类B,这里类B的情况就是对应位置有元素了,使用equals来判断是否相等,因为类B没有写equals方法,使用object的equals方法来判断,得到两个元素是不等的,所以在set中hashcode得到的索引位置建立了单链表,串联起来-->会造成set非常臃肿
  • 重写了hashcode和equals方法(规范重写:既如果hashcode相等,则equals一定相等),如同刚刚的类C,所以最终输出只有一个C

下面我来画图让大家看的更加直观,对于刚刚的插入:
Java探究HashSet中如何完成同一个对象不重复出现
大概的讲解就是这么多了,我知识水平有限....

上一篇:八.面向对象的思想(四)


下一篇:java中equals和hashCode方法为什么总是需要重写?