一次性看懂Equals和HashCode的用法与注意事项!面试必问基础!!

一次性看懂Equals和HashCode的用法与注意事项

一直对Equals和HashCode的概念及用法很是模糊,现在有时间,搜集了一下资料,进行了归纳总结。

关于Equals

Object类中默认的实现方式是 : return this == obj 。那就是说,只有this 和 obj引用同一个对象,才会返回true。

而我们往往需要用equals来判断 2个对象是否等价,而非验证他们的唯一性。这样我们在实现自己的类时,就要重写equals,当然重写equals的同时最好重写对应的hashCode,尤其在堆散列存储结构,像hashSet,HashMap,HashTable等这些相关操作的时候,则java明确规定了重写equal必须重写hashCode,下面会对这块进行相关说明。

Java语言规范要求equals需要具有如下的特性:

    自反性:对于任何非空引用 x,x.equals(x) 应该返回 true。
    对称性:对于任何引用 x 和 y,当且仅当 y.equals(x) 返回 true,x.equals(y) 也应该返回 true。
    传递性:对于任何引用 x、y 和 z,如果 x.equals(y)返回 true,y.equals(z) 也应返回同样的结果。
    一致性:如果 x 和 y 引用的对象没有发生变化,反复调用 x.equals(y) 应该返回同样的结果。
    对于任意非空引用 x,x.equals(null) 应该返回 false

重写equals方法时要遵循以上规范,例如:如下用法

  public boolean equals(Object obj) {
    
    
            if (this == obj) return true;
            if ((obj==null)||(this.getClass()!=obj.getClass()))
                return  false;
                User user = (User) obj;
            return this.id==user.id&&this.name.equals(u ser.name);
        }

关于HashCode

关于HashCode的作用及注意事项,看完这篇完整就够了,我觉得看了后觉得这个博主写的很到位,推荐看一下
https://blog.csdn.net/lijiecao0226/article/details/24609559

正确重写HashCode

参考文档:https://blog.csdn.net/benjaminzhang666/article/details/9468605#
注意:重写HashCode的时候不能重写成相同的值,对于两个不相同的对象(这里指内部成员属性值不相同),那么就没必要比较其equals,应该在重写HashCode的时候都把尽量把成员属性都计算在内好点,这样在进行散列存储结构的集合操作的时候就不用去计算其equals,直接根据HashCode就可以判断是否将对象加入到集合中,当然对于成员较多的对象HashCode则越复杂,也会影响性能

下面给出例子说明

重写HashCode时返回一样的值

class  Student {
    
    
    private String name;
    private int age;

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

    public boolean equals(Object obj) {
    
    
        System.out.println(this.name +"开始比较equals");
        if (this==obj)
            return true;
        if (obj!=null&&!(getClass()!=obj.getClass()))
            return false;
        Student student = (Student) obj;
        return this.name.equals(student.name)&&this.age==student.age;
    }

    public int hashCode(){
    
    
        System.out.println(this.name +"开始计算HashCode");
        return 31*age;
    }
}


public class HashCodeAndEqualsTest {
    
    
    public static void main(String[] args) {
    
    
        Set<Student> students = new HashSet<>();
        Student student1 = new Student("菜鸟", 25);
        Student student2 = new Student("大佬", 25);
        students.add(student1);
        students.add(student2);

       System.out.println(students);
    }

}

运行结果:

菜鸟开始计算HashCode
大佬开始计算HashCode
大佬开始比较equals
[Student{
    
    name='菜鸟', age=25}, Student{
    
    name='大佬', age=25}]

重写HashCode时将所有的属性计算在内,修改hashCode

    public int hashCode(){
    
    
        System.out.println(this.name +"开始计算HashCode");
        return 31*age+this.name.hashCode();
    }

运行结果:

菜鸟开始计算HashCode
大佬开始计算HashCode
[Student{
    
    name='大佬', age=25}, Student{
    
    name='菜鸟', age=25}]

可看到,对于成员变量不同的对象,第二中方式比第一种少了计算equals,提高了计算性能

总结:

  1. 重写HashCode的时候不应该返回同一个值,而应该将类的成员属性包括在内
  2. 重写HashCode的时候可将类的成员属性进行线性组合,即:hash = 属性****1的int形式+ C1*属性2的int形式+ C2*属性3的int形式+ …(C1,C2为系数,学过线性代数的更清楚一点~)
  3. 注意,拼接之后的数值不能超过整形的表达范围。
  4. 对于引用属性,可直接调用其HashCode即可,就像字符串类型的,直接拿来引用即可
  5. 采用线性组合可避免不同对象参数hash冲突,提高散列存储结构的利用率

猜你喜欢

转载自blog.csdn.net/qq_38338409/article/details/120391220
今日推荐