本来是要梳理在hash集合中,是如何利用equals()和hashCode()判定元素是否重复,但是要说equals()就要谈==,索性一起总结。
参考博文:https://www.cnblogs.com/skywang12345/p/3324958.html
强烈推荐一看。
equals()和==
当是普通数据类型(int,double,char等),==是直接比较值,如果是引用类型数据(String,对象),==是地址比较。
查看超类Object中的源码:
public boolean equals(Object obj) {
return (this == obj);
}
可见Object中的equals()和==没有区别。
看equals()在String和AbstractSet中的源码:
二者都覆写了equals()方法,在进行==比较的基础上,还有其他比较。
以String为例,如果两个字符串==返回true,equals()必定返回true;如果==返回false,那么会对字符串中的字符逐一比较,如果字符都相同,equals返回true。
equals()和hashCode()
equals()和hashCode()之间并没有关系,但在hash table算法的集合(hashSet,hashMap等)中,只有当equals()返回为ture,并且两个对象的hashCode()相同,HashSet才会判定为重复元素。
看一下在Set中,关于两个不可重复(duplicate)元素的描述:
/**
* A collection that contains no duplicate elements. More formally, sets
* contain no pair of elements <code>e1</code> and <code>e2</code> such that
* <code>e1.equals(e2)</code>, and at most one null element. As implied by
* its name, this interface models the mathematical <i>set</i> abstraction.
//...
**/
set不可以有两个元素e1.equals(e2),如果e1,e2属性都相同,在开发者看来,e1和e2是相同的对象,但是在自定义类中不重写equals()方法的话,那么equals()只进行地址比较(使用的是Object中的equals()方法),就会判定e1和e2是两个不同对象,这显然不符合开发者的需求,所以在自定义类中需要重写equals()方法,将对象内容逐一比较。
但equals()的结果为true,不一定说e1和e2就是两个不同元素了。还要看hashCode。
看下面的代码示例:
public class Test {
public static void main(String[] args) {
HashSet<Person> hs = new HashSet<Person>();
Person e1 = new Person("龙根子", 13);
Person e2 = new Person("龙根子", 13);
Person e3 = new Person("罗大吊", 17);
hs.add(e1);
hs.add(e2);
hs.add(e3);
System.out.println("e1和e2是否相同:" + e1.equals(e2));
System.out.println("e1_hashCode:" + e1.hashCode());
System.out.println("e2_hashCode:" + e2.hashCode());
Iterator<Person> iterator = hs.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
class Person {
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String name;
public int age;
// 重写equals()方法,将对象属性逐一比较,如果都相同,返回true
@Override
public boolean equals(Object obj) {
super.equals(obj);
Person per = (Person) obj;
if (this.name == per.name && this.age == per.age) {
return true;
} else {
return false;
}
}
@Override
public String toString() {
return this.name + "--" + this.age;
}
}
输出内容:
e1和e2是否相同:true
e1_hashCode:118352462
e2_hashCode:1550089733
龙根子--13
罗大吊--17
龙根子--13
可以看到,即使equals()为true,还是存入了重复的对象。
这是因为二者的hashCode不同,所以HashSet在添加e1和e2的时候,还是认为二者不同。
再重写hashCode()方法:
public class Test {
public static void main(String[] args) {
HashSet<Person> hs = new HashSet<Person>();
Person e1 = new Person("龙根子", 13);
Person e2 = new Person("龙根子", 13);
Person e3 = new Person("罗大吊", 17);
hs.add(e1);
hs.add(e2);
hs.add(e3);
System.out.println("e1和e2是否相同:" + e1.equals(e2));
System.out.println("e1_hashCode:" + e1.hashCode());
System.out.println("e2_hashCode:" + e2.hashCode());
Iterator<Person> iterator = hs.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
class Person {
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String name;
public int age;
// 重写equals()方法,将对象属性逐一比较,如果都相同,返回true
@Override
public boolean equals(Object obj) {
super.equals(obj);
Person per = (Person) obj;
if (this.name == per.name && this.age == per.age) {
return true;
} else {
return false;
}
}
//重写hashCode()
@Override
public int hashCode() {
//二者的name,age如果相同的话
//返回nameHash^age的值也一定相同
int nameHash = name.toUpperCase().hashCode();
return nameHash ^ age;
}
@Override
public String toString() {
return this.name + "--" + this.age;
}
}
输出结果:
e1和e2是否相同:true
e1_hashCode:40114077
e2_hashCode:40114077
罗大吊--17
龙根子--13
可以看到e1.equals()为true,e1和e2的hashCode相同,所以HashSet判定二者为重复(duplicate)元素,所以不会重复存入。