equals()和hashCode()使用总结
equals()
- Object类中的equals方法和“==”是一样的,没有区别,即俩个对象的比较是比较他们的栈内存中存储的内存地址。
- 而String类,Integer类等等一些类,是重写了equals方法,才使得equals和“==不同”,他们比较的是值是不是相等。
- 当自己创建类时,自动继承了Object的equals方法,要想实现不同的等于比较,必须重写equals方法。
- 我们在判断俩个对象逻辑上是否相等,即对象的内容是否相等不能直接使用继承于Object类的equals()方法,我们必须得重写equals()方法,改变这个方法默认的实现。
- 示例
package cn.galc.test;
public class TestEquals {
public static void main(String[] args) {
/**
* 这里使用构造方法Cat()在堆内存里面new出了两只猫,
* 这两只猫的color,weight,height都是一样的,
* 但c1和c2却永远不会相等,这是因为c1和c2分别为堆内存里面两只猫的引用对象,
* 里面装着可以找到这两只猫的地址,但由于两只猫在堆内存里面存储在两个不同的空间里面,
* 所以c1和c2分别装着不同的地址,因此c1和c2永远不会相等。
*/
Cat c1 = new Cat(1, 1, 1);
Cat c2 = new Cat(1, 1, 1);
System.out.println("c1==c2的结果是:"+(c1==c2));//false
System.out.println("c1.equals(c2)的结果是:"+c1.equals(c2));//false 此时比较的内存地址
}
}
//没有重写equals
class Cat {
int color, weight, height;
public Cat(int color, int weight, int height) {
this.color = color;
this.weight = weight;
this.height = height;
}
}
//先判断比较对象是否为null—>判断比较对象是否为要比较类的实例—–>比较俩个成员变量是否完全相等。
public class TestEquals {
public static void main(String[] args) {
/**
* 这里使用构造方法Cat()在堆内存里面new出了两只猫,
* 这两只猫的color,weight,height都是一样的,
* 但c1和c2却永远不会相等,这是因为c1和c2分别为堆内存里面两只猫的引用对象,
* 里面装着可以找到这两只猫的地址,但由于两只猫在堆内存里面存储在两个不同的空间里面,
* 所以c1和c2分别装着不同的地址,因此c1和c2永远不会相等。
*/
Cat c1 = new Cat(1, 1, 1);
Cat c2 = new Cat(1, 1, 1);
System.out.println("c1==c2的结果是:"+(c1==c2));//false
System.out.println("c1.equals(c2)的结果是:"+c1.equals(c2));//false 此时比较的内存地址
}
}
//重写equals
class Cat {
int color, weight, height;
public Cat(int color, int weight, int height) {
this.color = color;
this.weight = weight;
this.height = height;
}
/**
* 这里是重写相等从Object类继承下来的equals()方法,改变这个方法默认的实现,
* 通过我们自己定义的实现来判断决定两个对象在逻辑上是否相等。
* 这里我们定义如果两只猫的color,weight,height都相同,
* 那么我们就认为这两只猫在逻辑上是一模一样的,即这两只猫是“相等”的。
*/
public boolean equals(Object obj){
if (obj==null){
return false;
}
else{
/**
* instanceof是对象运算符。
* 对象运算符用来测定一个对象是否属于某个指定类或指定的子类的实例。
* 如果左边的对象是右边的类创建的对象,则运算结果为true,否则为false。
*/
if (obj instanceof Cat){
Cat c = (Cat)obj;
if (c.color==this.color && c.weight==this.weight && c.height==this.height){
return true;
}
}
}
return false;
}
}
HashCode
- map的数据结构是数组+链表的结构(jdk1.8是数组+链表+红黑树),hashcode用来定位数组下标索引,找到对象存放的桶,然后遍历桶内链表、树,找到对应的元素。
- Object默认的hashcode实现对于不同的对象会返回不同的值,因为是根据内存来得到的hashcode,因此,在上面那个例子中,不同的对象(即使同一个类型)有着不同的hashcode;
- 相等的两个对象他们的hashCode()肯定相等,也就是用equal()对比是绝对可靠的
- hashCode()相等的两个对象他们的equal()不一定相等,也就是hashCode()不是绝对可靠的。
- 在set存储的是不重复的对象,而判断对象是否相等是调用的hashCode和equals进行判断的,所以,如果要判断两个对象逻辑上的不相等,那就必须重写这两个方法。
- 所以在向set中添加对象时,必须重新这两个方法,且一般情况下,这两个方法要一起重写。
blic class ConflictHashCodeTest2{
public static void main(String[] args) {
// 新建Person对象,
Person p1 = new Person("eee", 100);
Person p2 = new Person("eee", 100);
Person p3 = new Person("aaa", 200);
Person p4 = new Person("EEE", 100);
// 新建HashSet对象
HashSet set = new HashSet();
set.add(p1);
set.add(p2);
set.add(p3);
// 比较p1 和 p2, 并打印它们的hashCode()
System.out.printf("p1.equals(p2) : %s; p1(%d) p2(%d)\n", p1.equals(p2), p1.hashCode(), p2.hashCode());
// 比较p1 和 p4, 并打印它们的hashCode()
System.out.printf("p1.equals(p4) : %s; p1(%d) p4(%d)\n", p1.equals(p4), p1.hashCode(), p4.hashCode());
// 打印set
System.out.printf("set:%s\n", set);
}
/**
* @desc Person类。
*/
private static class Person {
int age;
String name;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return name + " - " +age;
}
/**
* @desc重写hashCode
*/
@Override
public int hashCode(){
int nameHash = name.toUpperCase().hashCode();
return nameHash ^ age;
}
/**
* @desc 覆盖equals方法
*/
@Override
public boolean equals(Object obj){
if(obj == null){
return false;
}
//如果是同一个对象返回true,反之返回false
if(this == obj){
return true;
}
//判断是否类型相同
if(this.getClass() != obj.getClass()){
return false;
}
Person person = (Person)obj;
return name.equals(person.name) && age==person.age;
}
}
}