(简单的介绍一下)
对于自定义对象而言,我们要重写 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!!!
输出结果如下图:
惊不惊喜,意不意外。
首先我们需要知道的是,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的源码,解释的话,可以点点这个小东西。
在这里我们只讲,与我们有关的东西,因为,多的我也不懂。
对于我而言,我把这里的 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);
}
}
}
我在原有的代码基础上,增加了一条语句。
为什么会是这样呢?为什么还是没有调用equals方法呢?为什么会输出两个结果呢?
原因如下:
1,因为我在 add(p2) 之前将对象的 name 的值进行了修改,所以,p1得到哈希值,并其存入,当p2进行哈希值的计算时(此时p2的哈希值与p1的哈希值是不同的),发现哈希值对应的位置上没有元素,所以直接存入,无需调用equals方法。
2,因为他们是同一地址对象,所以,改一个,都将发生变化,所以输出的都是 cxfcxf 20。
最后我们可以得出如下结论:
(以下是我个人看法,如果不对,请留言:)
((通过上面我们对HashSet集合存入同一地址元素的分析,我们可知,当存入同一地址元素时,比较完,哈希值后(对于同一地址元素,他们哈希值一般情况下是相同的),会比较他们的地址是否相同,如果相同则舍弃,也不会调用equals()方法,如果不同,则调用equals()方法。))
先进行调用 hashCode()方法,判断在该位置处是否有元素的存入,若没有,我们则直接存入,
若有,则会比较他们的地址是否相同,如果相同,则舍弃,也不会调用equals()方法,如果不同,则调用equals()方法。
若equals()方法,得出的结果,如果是相同,则舍弃,如果不同,则存入。
总之就是,先 hashCode() 然后 比较地址 最后 equals() 。
觉得写得好,点个赞呗!!!