重写自定义类的equals()方法,可以自定义对象相同的条件
为什么要重写equals() 方法?
- 会有判断两个元素是否相等 equals() 时找不到的问题时。
- 想使用系统中提供的方法更方便的写代码时。
这是因为没有重写自定义类型的equals()方法
判断两个元素是否相等 equals() 时找不到的问题时
//现在有一个Studnet类
public class Student {
public String name;
public int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
//执行
public static void main(String[] args) {
//创建两个我们自以为相同的学生对象
Student s1 = new Student("小明", 18);
Student s2 = new Student("小明", 18);
System.out.println(s1.equals(s2)); //结果为 false
}
此时发现 结果是false ,而不是我们想象的true 为什么呢?
因为如果不重写
equals()
方法,使用的是Object中的equals方法,即默认情况下会按照对象的内存地址进行比较
Student s3 = s1;
System.out.println(s1.equals(s3)); //true.
Object中的equals源码
public boolean equals(Object obj) {
return (this == obj);
}
这样可以看出 其实内部做对象比较的时候还是使用的 == 在比较
想使用系统提供的方法快速编写代码时
这里用List集合举例
//现有一个 学生集合 存了三个学生
List<Student> list =new ArrayList<>();
list.add(new Student("小明",18));
list.add(new Student("小红",20));
list.add(new Student("小刚",22));
此时我想添加一个学生并不想让姓名重复时我们该怎么办?
可能第一时间想到的就是遍历,
//遍历 .....
for(Student s : list){ ... }
也可能想到了List中提供的方法 contains indexOf …
但是你突然发现,都是false ,或者就是 -1 之类的,反正就是List没有这个对象
此时因为什么?因为你的自定义类没有重写equals方法,系统默认比较对象的内存地址。
如何重写equals方法
如下 在 Student 类中
public class Student {
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof Student))
return false;
Student student = (Student) o;
return Objects.equals(name, student.name);
//注意这个是Objects 不是Object 加了s的
}
}
equals里边的内容自已设置。这里只考虑返回值
return true; 表示两个对象相同
return false; 表示两个对象不同
上图中的意思时 如果两个对象的name属性值相同就表示学生是一个学生。
上图代码中的Objects类中的静态equals方法的解释
直接看源码:
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
//使用的还是你传入对象类型的equals方法
// 如果该类型没有重写equals方法,会上找父类的equals方法直到Object
}
可以看出使用的还是传入对象的equals方法
此时会有同学问,a b明明是Object类型,为什么不是直接使用的是Object的equals方法而是实际对象类型的?
因为override是重写,你已经把父类的equals方法给覆盖了,这就是java中的多态。
操作下试试看
Student s1 = new Student("小明", 18);
Student s2 = new Student("小明", 20);
System.out.println(s1.equals(s2));//true
List中:
List<Student> list =new ArrayList<>();
list.add(new Student("小明",18));
list.add(new Student("小红",20));
list.add(new Student("小刚",22));
Student s =new Student("小明",18);
System.out.println(list.contains(s)); //true
Student s2 =new Student("小红",666);
System.out.println(list.contains(s2));//true
这时候会有人问了?我重写了equals方法和contains有什么关系?
问的好!
那是因为contains方法在执行时是依赖equals方法进行判断的
那么List还有那些方法在执行时是依赖equals方法的呢?
那可太多了
//间接依赖
int hashCode()
//直接依赖
boolean contains(Object o)
int indexOf(Object o)
int lastIndexOf(Object o)
boolean remove(Object o)
<E> remove(int index)
boolean containsAll(Collection<?> c)
boolean removeAll(Collection<?> c)
这些方法我们都可以按照我们重写equals的判断模式直接使用
重写equals时候为什么建议还要方法重写hashCode()?
因为重写了equals后还是用系统默认的hashCode算法时,不同的对象的hashCode可能是相同的,虽然概率极小。但是也要重写hashCode算法,也就是重写hashCode方法,来最大限度的减少概率,让其趋近于0;
代码界的一般约定:equals比较时相同的对象必须具有相同的哈希码。
如果hashCode相同的时候会对后续的比如HashMap有影响,默认是通过内存地址来进行算法计算的,通常会改为基于对象的字段值来计算哈希码
@Override
public int hashCode() {
return Objects.hash(name);
}
扩展:重写toString()方法 进行展示
@Override
public String toString() {
return "姓名:"+this.name+"\t年龄:"+this.age;
}
//main中
Student s =new Student("小明",18);
System.out.println(s); //系统会默认调用toString()
//System.out.println(s.toString()); //不信可以试试,和上边一样的
输出:姓名:小明 年龄:18
如果不重写就是输出了地址值