Java题集 - equals() 和 == 及equals() 和 hashcode() 的区别与联系

文章详细阐述了Java中`==`与`equals()`方法的区别,`equals()`默认行为以及何时需要重写。同时,解释了`hashcode()`在散列存储结构如HashSet、HashMap中的作用,强调了当重写equals()时必须重写`hashcode()`以确保数据唯一性,避免哈希冲突导致的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、==和equeals的区别?

== 和 equals 的区别 :

  • ==是运算符,如果是基本数据类型,则比较的是其存储的值是否相等;如果是引用数据类型,则比较的是所指向对象的地址值是否相等(是否是同一个对象)。

  • equals的作用是判断两个对象是否相等,如果对象重写了equals()方法,比较两个对象的内容是否相等;如果没有重写,比较所指向对象的地址值是否相同 ,等价于“==”。

1、 equals是Object的方法,本质上相当于==,比较的是引用类型的变量所指向的对象的地址值是否相等,不能用于比较基本数据类型,源码如下:

public boolean equals(Object obj) {
    // 实际上返回的就是使用==进行比较的结果
    return (this == obj);
}

2、所有的类都继承Object,equals是Object对象中的非final方法,目的就是被用来覆盖(override)的,所以当我们使用equal()方法进行比较的时候,需要关注的就是这个类有没有重写Object中的equals()方法,一般情况下,类都会重写equals方法用来比较两个对象的内容是否相等。比如String类,Integer类中的equals()是被重写了,比较的是对象的值。String类的equals()方法如下:

public boolean equals(Object anObject) {
    // 先使用==比较两个String对象的地址值是否相同,如果地址值相同,直接返回true
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        // 比较两个String对象的内容是否相同
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

3、自定义一个Person类,没有重写Object类的equals()方法 :

public class Person {
    String name;
    int age;

    public Person(String name,int age) {
        this.name = name;
        this.age = age;
    }
}
public class Main {
    public static void main(String[] args) {
        Person person1 = new Person("张三",18);
        Person person2 = new Person("张三",18);
        System.out.println(person1.equals(person2)); // false
    }
}

4、自定义一个Person类,重写Object类的equals()方法 :

public class Person {
    String name;
    int age;

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

    @Override
    public boolean equals(Object o) {
        // 先使用==比较地址值是否相同,如果相同说明是同一个对象,直接返回true
        if (this == o) {
            return true;
        }
        if(o instanceof Person){
            Person person = (Person) o;
            return name.equals(person.name) && age==person.age;
        }
        return false;
    }
}
public class Main {
    public static void main(String[] args) {
        Person person1 = new Person("张三",18);
        Person person2 = new Person("张三",18);
        System.out.println(person1.equals(person2)); // true
    }
}

2、hashcode()和equals()方法的关系?

两个对象的hashcode相同,则equals一定为true对吗?
你重写过 hashcode 和 equals 么,为什么重写equals时必须重写hashCode方法?equals和hashCode都是Object对象中的非final方法,它们设计的目的就是被用来覆盖(override)的,所以在程序设计中还是经常需要处理这两个方法。

(1) hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。

(2) equals它的作用也是判断两个对象是否相等,如果对象重写了equals()方法,比较两个对象的内容是否相等;如果没有重写,比较两个对象的地址是否相同,价于“==”。

一般一个类的对象如果会存储在HashTable,HashSet,HashMap等散列存储结构中,那么重写equals后最好也重写hashCode,否则会导致存储数据的不唯一性(存储了两个equals相等的数据)。而如果确定不会存储在这些散列结构中,则可以不重写hashCode。但是个人觉得还是重写比较好一点,谁能保证后期不会存储在这些结构中呢,况且重写了hashCode也不会降低性能。

1、一个类的对象如果不会存储在HashTable,HashSet,HashMap等散列存储结构中

在这种情况下, 该类的hashCode()equals() 没有半毛钱关系的!。equals() 用来比较该类的两个对象是否相等,而hashCode() 则根本没有任何作用,所以,不用理会hashCode(),但就像前面说的,最好也重写hashcode()方法,万一以后用到了呢?

public class Person {
    String name;
    int age;

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

    @Override
    public boolean equals(Object o) {
        // 先使用==比较地址值是否相同,如果相同说明是同一个对象,直接返回true
        if (this == o) {
            return true;
        }
        if(o instanceof Person){
            Person person = (Person) o;
            return name.equals(person.name) && age==person.age;
        }
        return false;
    }
}
public class Main {
    public static void main(String[] args) {
        Person p1 = new Person("eee", 100);
        Person p2 = new Person("eee", 100);
        System.out.println(p1.equals(p2)); // true
        System.out.println("p1:"+ p1.hashCode() +" p2:"+p2.hashCode()); // p1:68545 p2:68545
    }
}

从结果也可以看出,p1和p2相等的情况下,hashCode()也不一定相等。

2、一个类的对象如果会存储在HashTable,HashSet,HashMap等散列存储结构中

在这种情况下,该类的“hashCode() 和 equals() ”是有关系的:

(1) 若两个对象equals返回true,则hashCode必须相等,否则会存储重复的元素

(2) 若两个对象hashCode相等,则equals不一定返回true,因为在散列表中,hashCode()相等,即两个键值对的哈希值相等。然而哈希值相等,并不一定能得出键值对相等,此时就出现所谓的哈希冲突场景。

public class Person {
    String name;
    int age;

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

    @Override
    public boolean equals(Object o) {
        // 先使用==比较地址值是否相同,如果相同说明是同一个对象,直接返回true
        if (this == o) {
            return true;
        }
        if(o instanceof Person){
            Person person = (Person) o;
            return name.equals(person.name) && age==person.age;
        }
        return false;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Main {
    public static void main(String[] args) {
        Person p1 = new Person("eee", 100);
        Person p2 = new Person("eee", 100);

        HashSet<Person> set = new HashSet<>();
        set.add(p1);
        set.add(p2);

        System.out.println(p1.equals(p2)); // true
        System.out.println(p1.hashCode()); // 685325104
        System.out.println(p2.hashCode()); // 460141958
        System.out.println(set);// [Person{name='eee', age=100}, Person{name='eee', age=100}]
    }
}

HashSet用于存放不重复的元素,我们在Person类中重写了equals()方法,但是发现:HashSet中仍然有重复元素:p1 和 p2,为什么会出现这种情况呢?因为p1和p2的内容虽然相等,但是它们的hashCode()不等,所以HashSet在添加p1和p2的时候,认为它们不相等,它们被添加到了HashSet的两个不同桶中。

在Person类中重写hashcode()方法:

public class Person {
    String name;
    int age;

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

    @Override
    public boolean equals(Object o) {
        // 先使用==比较地址值是否相同,如果相同说明是同一个对象,直接返回true
        if (this == o) {
            return true;
        }
        if(o instanceof Person){
            Person person = (Person) o;
            return name.equals(person.name) && age==person.age;
        }
        return false;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int hashCode() {
        int nameHash = name.toUpperCase().hashCode();
        return nameHash ^ age;
    }
}
public class Main {
    public static void main(String[] args) {
        Person p1 = new Person("eee", 100);
        Person p2 = new Person("eee", 100);
        Person p3 = new Person("EEE", 100);

        HashSet<Person> set = new HashSet<>();
        set.add(p1);
        set.add(p2);
        set.add(p3);

        System.out.println(p1.equals(p2)); // true
        System.out.println("p1:"+p1.hashCode() + ", p2:"+ p2.hashCode()); // p1:68545, p2:68545
        System.out.println(p1.equals(p3));  // false
        System.out.println("p3:"+p1.hashCode() + ", p4:"+ p3.hashCode()); // p3:68545, p4:68545
        System.out.println(set); // [Person{name='eee', age=100}, Person{name='EEE', age=100}]
    }
}

p1和p2的hashCode()相等,equals()方法也返回true,则p1和p2相等。

p1和p3的hashCode()相等;但equals()方法返回false。则p1和p3不相等。

此时,HashSet中没有重复元素,所以,如果类使用再散列表的集合对象中,要判断两个对象是否相同,除了要覆盖equals()之外,也要覆盖hashCode()函数。否则,equals()无效,为什么吗?我们来看看HashSet的源码:

可以看出,hashSet使用的是hashMap的put方法,而hashMap的put方法中:首先计算添加元素key的hash值与桶中元素key的hash值是否相同,如果相同,再通过equals()比较key值是否相同,如果相同,返回同一个对象。

if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))

3、总结

如果一个类的对象如果不会存储在HashTable,HashSet,HashMap等散列存储结构中 : hashcode()方法和equals()方法没有关系,比较两个对象是否相等时,重写equals()方法即可。

如果一个类的对象如果不会存储在HashTable,HashSet,HashMap等散列存储结构中 : hashcode()方法和equals()方法就必须满足下面的关系 。

(1) equals() 和hashCode() 需要同时覆盖,重写equals()方法就必须重写hashcode()方法;

(2) 若两个对象的equals()方法返回true,则hashCode必须相等,否则会存储重复的元素,因为hashcode不同这两个对象会存储到HashSet不同的桶中;

(3) 若两个对象equals返回false,hashcode可能相等,即出现哈希冲突,不同的两个对象分到同一个桶中;

(4) 若两个对象hashCode相等,则equals不一定返回true,即出现哈希冲突,不同的两个对象分到同一个桶中;

(5) 若两个对象hashCode不等,则equals一定返回false,否则会导致存储重复元素 ;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我一直在流浪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值