Java中equals()方法的使用

在这里插入图片描述

Java中equals()方法的使用


equals()方法的使用

  • ==​ 运算符:
  1. 可以使用在基本数据类型和引用数据类型中

  2. 如果比较的是基本数据类型,比较的是两个变量保存的数据是否相等。
    *char = 10​此处是ASCII码
    ​ 如果比较的是引用数据类型,不记得是两个对象的地址值是否相同,即两个引用对象是否指向同一个对象实体
    字符虽然相同,但是比较的是地址值,所以false :

    String str1 = new String("123");
    String str2 = new String("123");
    System.out.println(str1 == str2);//false
    
  • equals()​方法的使用
  1. 是一个方法,并非运算符

  2. 只适用于引用数据类型

  3. Object​类中equals( )​的定义:

     public boolean equals(object obj) { 
    		return (this == obj);  //this是指类的当前实例
     }
    

    说明:·类中定义的equals()和==的作用是相同的:比较两个对象的地址值是否相同。即两个引用是否指向同一个对象实体

  4. 对于像String​、Data​、File​、包装类等都重写了Object​类中的equals​()方法,重写以后的方法比较的不是两个引用的地址,而是比较的两个对象的“实体内容”是否相同。

  5. 通常情况下,我们自定义的类如果使用equals(),通常是比较两个对象的“实体内容”是否相同,那么就需要对Object类中的equals()进行重写。

    //此处是在Order类中
    @Override
    public boolean equals(object obj){ 
    if(this == obj){ 
    return true;
    }
    if(obj instanceof Order){ 
      Order order =(Order)obj;
      return this.orderId == order.orderId &&
        this.orderName.equals(order.orderName);
      //基本数据类型用“==”, 引用数据类型用equals
    }
    

    通常可以通过Generate自动生成自定义equals方法 macOS
    快捷键control+return

    如果类中只有一个属性,则直接可以 order.equals(order)

注意:

  1. ==​既可以比基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型就是比较内存地址
  2. equals​的话,它是属于java.lang.Object​类里面的方法,如果该方法没有被重写过默认也是==​;我们可以看到String​等类的equals​方法是被重写过的,而且String​类在日常开发中用的比较多,久而久之,形成了equals​是比较值的错误观点。
  3. 具体要看自定义类里有没有重写Object​的equals​方法来判断。
  4. 通常情况下,重写equals​方法,会比较类中的相应属性是否都相等。

String类的equals比较:

String str1 = new String("hello");
String str2 = new String("hello");

System.out.println(str1 == str2);         // false (比较引用)
System.out.println(str1.equals(str2));    // true  (比较内容)

自定义类的equals:

public class Student {
    private String name;
  
    // 没有重写equals,将使用Object类的equals方法(等同于==)
    // 此时比较的是引用而不是内容
}

public class Student {
    private String name;
  
    // 重写equals,比较内容
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Student student = (Student) obj;
        return Objects.equals(name, student.name);
    }
}

常见类的equals行为:

// String - 比较字符串内容
String s1 = "hello";
String s2 = "hello";
s1.equals(s2);  // true

// Integer - 比较数值
Integer i1 = new Integer(123);
Integer i2 = new Integer(123);
i1.equals(i2);  // true

// ArrayList - 比较列表内容
ArrayList<String> list1 = new ArrayList<>(Arrays.asList("a", "b"));
ArrayList<String> list2 = new ArrayList<>(Arrays.asList("a", "b"));
list1.equals(list2);  // true

== 与 equals 的区别:

String s1 = new String("hello");
String s2 = new String("hello");
String s3 = "hello";
String s4 = "hello";

System.out.println(s1 == s2);      // false(不同对象引用)
System.out.println(s1.equals(s2)); // true(相同内容)
System.out.println(s3 == s4);      // true(字符串常量池中的同一引用)
System.out.println(s3.equals(s4)); // true(相同内容)

重要提示:

  • 如果自定义类需要比较内容,应该重写equals()方法

  • 重写equals()时通常也需要重写hashCode()方法

  • Objects.equals()是空安全的比较方法,推荐使用

  • ==比较基本类型时比较值,比较引用类型时比较引用地址

现在需要纠正上面代码示例存在的问题,这个很细节

在Java中重写equals方法时必须重写hashCode方法,这是因为这两个方法之间存在一个重要的契约关系。让我为您详细解释:

  1. equals和hashCode的基本契约:
  • 如果两个对象通过equals()方法比较是相等的,那么它们的hashCode()方法必须返回相同的值
  • 如果两个对象的hashCode()值不同,那么这两个对象一定不相等(equals()方法返回false)
  • 如果两个对象的hashCode()值相同,这两个对象不一定相等(可能会发生哈希碰撞)
  1. 实际影响:
  • 当我们使用HashMap、HashSet等基于哈希的集合类时,这个契约特别重要
  • 这些集合类首先使用hashCode()来确定元素的存储位置
  • 当发生哈希碰撞时,才会使用equals()方法进行进一步比较
  1. 如果违反契约会发生什么:
  • 假设只重写equals而不重写hashCode:

    • 两个对象通过equals()判断相等
    • 但它们的hashCode()可能不同
    • 在HashMap中会被存储到不同的位置
    • 导致同一个对象可能会被存储多次
    • 影响查找效率和正确性
  1. 示例:
class Person {
    private String name;
    private int age;
  
    // 只重写equals
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (!(obj instanceof Person)) return false;
        Person person = (Person) obj;
        return age == person.age && 
               Objects.equals(name, person.name);
    }
  
    // 没有重写hashCode
    // 将使用Object默认的hashCode实现
    // 这会导致即使equals相等的对象也可能有不同的hash值
}

// 使用示例
Person p1 = new Person("张三", 25);
Person p2 = new Person("张三", 25);

// equals返回true
System.out.println(p1.equals(p2)); // true

HashSet<Person> set = new HashSet<>();
set.add(p1);
set.add(p2);
// 会存入两个对象,因为hashCode不同
System.out.println(set.size()); // 2
  1. 正确的做法:
class Person {
    private String name;
    private int age;
  
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (!(obj instanceof Person)) return false;
        Person person = (Person) obj;
        return age == person.age && 
               Objects.equals(name, person.name);
    }
  
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
  1. 最佳实践:
  • 重写equals时必须重写hashCode
  • hashCode方法应该使用对象的关键属性(用于equals比较的属性)
  • 可以使用Objects.hash()方法来生成hashCode
  • IDE通常可以自动生成这两个方法
  • 如果类是不可变的,考虑缓存hashCode的值

总之,重写equals而不重写hashCode会违反Java集合框架的基本约定,可能导致基于哈希的集合类出现异常行为。为了保证程序的正确性和性能,这两个方法应该始终一起重写。

那现在有人会问了,前面的代码应该重写吗

public class Student {
    private String name;
  
    // 没有重写equals,将使用Object类的equals方法(等同于==)
    // 此时比较的是引用而不是内容
}

public class Student {
    private String name;
  
    // 重写equals,比较内容
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Student student = (Student) obj;
        return Objects.equals(name, student.name);
    }
}

这个问题很好!让我们分析两种情况:

第一种情况(没有重写equals):

public class Student {
    private String name;
    // 使用Object默认的equals方法
}

第二种情况(重写了equals):

public class Student {
    private String name;
  
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Student student = (Student) obj;
        return Objects.equals(name, student.name);
    }
}

在这种情况下,必须重写hashCode方法。因为:

  1. 重写后的equals是基于name属性的内容比较

  2. 而默认的hashCode方法仍然基于对象的内存地址

  3. 这会导致两个name相同的Student对象:

    • equals比较会返回true
    • 但hashCode可能返回不同的值
  4. 违反了equals和hashCode的契约

正确的做法应该是:

public class Student {
    private String name;
  
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Student student = (Student) obj;
        return Objects.equals(name, student.name);
    }
  
    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
}

实际影响示例:

// 使用没有重写hashCode的版本
Student s1 = new Student("张三");
Student s2 = new Student("张三");

System.out.println(s1.equals(s2)); // true

HashSet<Student> set = new HashSet<>();
set.add(s1);
set.add(s2);
System.out.println(set.size()); // 2,实际上应该是1

总结:

  1. 如果你使用Object默认的equals(比较引用),不需要重写hashCode
  2. 如果你重写了equals方法(比较内容),就必须重写hashCode
  3. 重写hashCode时,使用equals方法中用到的所有属性来计算哈希值
  4. 这样做是为了确保在使用HashSet、HashMap等基于哈希的集合类时能够正常工作

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值