Java基础:HashSet集合(多次添加同一地址对象而引发的问题)

HashSet集合:

(简单的介绍一下)
对于自定义对象而言,我们要重写  hashCode()  和   equals()  这两个方法。

去重的基本原理是:

先计算对象的哈希值,如果哈希值所对应的位置上没有 元素,

直接,存入集合当中,无需调用equals()方法,

如果有,调用equals()比较两个对象的内容是否相同,

如果相同,就舍弃,如果不同,就存入。

 

就在今天编程时当我想要存入相同地址的对象时去发生了一些问题。(源码如下:)

package HashSetDemo;
import java.util.HashSet;
import java.util.Iterator;

class Person{
	String name;
	int age;

	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}


	@Override	
	public int hashCode() {
		
		return name.hashCode()+((Integer)age).hashCode();
		
	}

	@Override
	public boolean equals(Object obj) {
		
		if(this == obj) {		
			System.out.println("因为,调用的两个对象的地址相同,所以返回true");
			return true;
			
		}
		if(!(obj instanceof Person))
			return false;
		Person p = (Person)obj;		
		return (this.name).equals(p.name) && this.age == p.age;
	}
}

public class HashSetDemo {
	public static void main(String[] args) {
		
		HashSet ha = new HashSet();
		
		Person test = new Person("cxf",20);
		
		Person p1 = test;
		
		Person p2 = test;

		ha.add(p1);
		
		ha.add(p2);

		for(Iterator it = ha.iterator();it.hasNext();) {
			Person pp = (Person)it.next();
			System.out.println(pp.name+"  "+pp.age);
		}
	}
}

 

在这里简单说明一下(关键代码)

HashSet ha = new HashSet();
Person test = new Person("cxf",20);
        
Person p1 = test;
Person p2 = test;

ha.add(p1);
ha.add(p2);

②:这是我复写的equals方法

    public boolean equals(Object obj) {
        
        if(this == obj) {        
            System.out.println("因为,调用的两个对象的地址相同,所以返回true");
            return true;
            
        }
        if(!(obj instanceof Person))
            return false;
        Person p = (Person)obj;        
        return (this.name).equals(p.name) && this.age == p.age;
    }

输出结果前,我们先分析分析:

我向集合set中添加了两个同一地址的对象,在没看到输出结果之前,我们先来分析一下,首先这两个对象会掉用hashCode(),因为是同一地址,我们不难发现他们的 哈希值 是相同的,所以他们要调用equals(),而在equals方法里面有一个判断条件是 if(this == obj),用于判断两个对象的地址是否相同,因为是同一地址对象,所以我们在Eclipse的返回结果中我们应该看到,会输出  "因为,调用的两个对象的地址相同,所以返回true"  这样的内容。然而真的是会有这条输出的语句吗?答案是 NO!!!

输出结果如下图:

 

Java基础:HashSet集合(多次添加同一地址对象而引发的问题)_第1张图片

惊不惊喜,意不意外。

 

下面我们就来讲一讲,为什么调用了equals方法却没有得到我们想要的结果。

首先我们需要知道的是,HashSet 的 底层是由 HashMap来实现的,所以解决这个问题我们需要查看一下JDK源码(相关源码截图如下)

首先我们先查看了HashSet里面当中 add()方法的源码如下:

我们可以清楚的看到,add方法里面调用了map的put方法。

接着我们查看 map里面put方法的源码,截图如下:

在这里呢,我们只是看见了,put里面调用了 hash方法和putVal方法,接着我们分别查看他们的源码截图如下:


hash方法的源码:

通过hash源码,我们可以得到如下结论:

1,HashSet集合 和 HashMap集合 可以存入 null 

2,hash(Object key)方法里面会自动调用 key.hashCode(),从而进一步说明,在使用HashSet的add方法时会自动调用对象的hashCode()方法,也进一步说明为什么要 复写 hashCode()方法的原因。

3,深入下去,查看hashCode()源码截图如下:

所以我们可以知道在,不重写 hashCode()方法时,我们得到哈希值与对象的地址有关。

 

putVal方法源码如下:(这个源码我认为很复杂,但不妨碍我们理解)

如果想进一步了解 putVal的源码,解释的话,可以点点这个小东西。

Java基础:HashSet集合(多次添加同一地址对象而引发的问题)_第2张图片

在这里我们只讲,与我们有关的东西,因为,多的我也不懂。

关键部分如下:

对于我而言,我把这里的 e 当做存放的位置,在这个 if语句里面,我们先比较

p.hash == hash,比较的是p和当前对象的哈希值,因为是同一地址对象,他们的哈希值是相等的,(也有可能不等),所以比较的结果是true所以我们接着比较 (k = p.key) == key 比较的是他们的地址是否相同,因为是同一地址对象,所以他们肯定是相同的,所以返回的结果是true,在加上这里是用  | |  连接起来的,所以不用调用equals方法,所以就进一步解释了,为什么?我们没有看到我们预期的返回结果,因为对于存入,相同地址的对象,在JDK源码里面,就没有给我们调用equal的机会。

 

由于本人比较来事,所以我又在原来的代码上进行了一点小小的改动,改动后的源码如下:
 

package HashSetDemo;
import java.util.HashSet;
import java.util.Iterator;

class Person{
	String name;
	int age;

	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}


	@Override	
	public int hashCode() {
		
		return name.hashCode()+((Integer)age).hashCode();
		
	}

	@Override
	public boolean equals(Object obj) {
		
		if(this == obj) {		
			System.out.println("因为,调用的两个对象的地址相同,所以返回true");
			return true;
			
		}
		if(!(obj instanceof Person))
			return false;
		Person p = (Person)obj;		
		return (this.name).equals(p.name) && this.age == p.age;
	}
}

public class HashSetDemo {
	public static void main(String[] args) {
		
		HashSet ha = new HashSet();
		
		Person test = new Person("cxf",20);
		
		Person p1 = test;
		
		Person p2 = test;

		ha.add(p1);
		
		p1.name="cxfcxf";
		
		ha.add(p2);

		for(Iterator it = ha.iterator();it.hasNext();) {
			Person pp = (Person)it.next();
			System.out.println(pp.name+"  "+pp.age);
		}
	}
}

 

我在原有的代码基础上,增加了一条语句。

Java基础:HashSet集合(多次添加同一地址对象而引发的问题)_第3张图片

不卖关子,直接上图(输出结果):

为什么会是这样呢?为什么还是没有调用equals方法呢?为什么会输出两个结果呢?

原因如下:
1,因为我在 add(p2) 之前将对象的 name 的值进行了修改,所以,p1得到哈希值,并其存入,当p2进行哈希值的计算时(此时p2的哈希值与p1的哈希值是不同的),发现哈希值对应的位置上没有元素,所以直接存入,无需调用equals方法。

2,因为他们是同一地址对象,所以,改一个,都将发生变化,所以输出的都是 cxfcxf 20。

 

最后我们可以得出如下结论:

(以下是我个人看法,如果不对,请留言:)

HashSet集合存入元素正真的去重机制是:

((通过上面我们对HashSet集合存入同一地址元素的分析,我们可知,当存入同一地址元素时,比较完,哈希值后(对于同一地址元素,他们哈希值一般情况下是相同的),会比较他们的地址是否相同,如果相同则舍弃,也不会调用equals()方法,如果不同,则调用equals()方法。))

先进行调用 hashCode()方法,判断在该位置处是否有元素的存入,若没有,我们则直接存入,

若有,则会比较他们的地址是否相同,如果相同,则舍弃,也不会调用equals()方法,如果不同,则调用equals()方法。

若equals()方法,得出的结果,如果是相同,则舍弃,如果不同,则存入。

总之就是,先  hashCode()  然后  比较地址   最后  equals() 

 

觉得写得好,点个赞呗!!!

 

 

 

 

 

你可能感兴趣的:(Java,HashSet)