欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

HashSet存储过程中如何排除不同的自定义对象?

程序员文章站 2022-03-11 16:24:50
...

HashSet

HashSet存储过程中如何排除不同的自定义对象?

先看一个小demo

public class Demo1 {
	public static void main(String[] args) {
		//Person4类是一个失血模型,它有姓名和年龄两个属性
		HashSet<Person4> set = new HashSet<>();
		set.add(new Person4("张三", 23));
		set.add(new Person4("张三", 23));
		set.add(new Person4("李四", 24));
		set.add(new Person4("李四", 24));
		set.add(new Person4("李四", 24));
		
		/*
		 * 直接打印,我们会发现,它把5个元素加入进去了,包括我们认为“重复”的元素
		 * 可是,HashSet不是号称不可重复吗?这是为什么?
		 */
		System.out.println(set);
	}
}

HashSet在存储自定义的对象比如Person4(含name和age属性)时,如果存放name和age值相同的对象时,它都会存放进去。因为我们把它当作是相同的元素是根据它的属性值判断,而程序会为每个new出来的对象计算出不同的hashcode码,所以它认为是不同的对象,都会存放进去。

如何让它有效排除我们认为的“所有属性值相同的对象就是同一个对象”在这种定义下的同一种对象呢?

首先我们要去了解它的底层实现,即:
它判断是否相同的时候,肯定用了equals方法,他在计算hashcode的时候,应该用到了hashCode方法,那么它的执行流程和内部是怎么实现的呢?

我们追溯HashSet的源码,首先我们用到了add方法,它在add的时候肯定会进行是否存在相同对象的判断。我按住ctrl+鼠标左键点进去,一层一层追溯,结果懵逼了,发现里面涉及了很多以我目前实力看不懂的代码,怎么办?

先不管三七二十一,因为根据之前看源码的经验,我发现如果一个方法的底层如果用了equals方法,那么它调用的是obj的equals方法。什么意思呢?就是说它其实调用的是被操作对象的方法,我们每一个Person4对象的equals方法,这样实现的目的应该是为了我们使用者可以方便地重写equals方法底层实现从而实现我们自己的个性化使用。
我直接在person4里重写了equals方法如下:

@Override
	public boolean equals(Object obj) {
		//先把obj强制转换成person4对象
		Person4 p = (Person4) obj;
		//改成根据各属性的值判断是否同一个对象
		return p.name.equals(this.name) && p.age == p.age;
	}

再运行了下程序,发现并不起作用,重复元素还是存在。

我再一咬牙,再把hashCode方法一并改写了:

@Override
	public int hashCode() {
		return 10;
	}

我让hashCode方法都返回10,发现重复元素都没了。

到了这里,不禁困惑,为什么这样就可以了呢?

后来,经过查阅资料,观看视频得知,hashSet底层的add方法大概流程是这样的:先比较hashcode码,不相同则插入成功,如果相同,则用equals比较。

再反观我们上面的程序,因为我让它hashcode都变成了10,所以他们最终都是通过equals比较的,equals被我重写,它就能够去掉重复的元素。

但是,实际开发中这样效率是不高的,我们应该让它尽可能少调用方法,达到效率的提升,特别是操作的元素数量特别多的时候。

所以,我们就应该让它在第一关:hashcode码判断的时候,就应该让不同的元素尽量返回不同的hashcode码,一次判断就结束。

如果你使用的是eclipse,那么可以利用它自带的便捷功能:自动重写equals和hashcode方法,并且实现了我们上面提的要求(它为我们考虑好了算法)。按住shift+alt+s,点击generate hashCode( ) and equals( ),他会自动重写我们的两个方法。你想学习它的算法怎么实现的,可以看它生成的代码。