Java对象的比较——equals方法,Comparable接口,Comparator接口

Java对象的比较——equals方法,Comparable接口,Comparator接口

1. equals方法

在判断两个整数是否相同时,我们可以使用以下方式:

System.out.println(1 == 2);
System.out.println(1 == 1);

如果输出true,则说明这两个整数相同;如果输出false,则说明这两个整数不相同

那么,如果将==用于判断两个对象,又会是怎样的情况呢?我们直接敲代码来看看!

public class Dog {
    private String name;
    private int age;

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
public class Test1 {
    public static void main(String[] args) {
        Dog dog1 = new Dog("zhangsan",1);
        Dog dog2 = new Dog("zhangsan",1);
        System.out.println(dog1 == dog2);
    }
}

运行结果:
在这里插入图片描述
当运行这段代码时,我们发现输出的是false。明明dog1和dog2的属性都一模一样,为什么输出false呢?莫慌,且听我慢慢道来!
在这里插入图片描述
画图来分析,dog1和dog2中存放的值并不相同,因此dog1 != dog2。而你之所以认为输出的应该是true,是因为你认为,两个对象的属性完全一致,所以dog1 == dog2.其实,并不是这样的,==判断的并不是对象的属性是否一致,而是判断两个引用指向的是否是同一个对象!

在Java中,一切皆对象。而dog1和dog2是对象的引用。==判断的并不是对象的属性是否一致,而是判断两个引用指向的是否是同一个对象!

因此,只有当两个引用指向同一对象才返回true;而如果两个引用指向不同的对象,即使两个对象的属性完全相同,返回依旧是false!

总结:

  • 如果==左右两边是基本数据类型变量,比较的是变量中的值是否相同
  • 如果==左右两边是引用数据类型变量,比较的是引用变量中的值是否相同,而引用变量中存放的是对象的地址,所以比较的就是引用变量中存放的地址是否相同(即判断两个引用指向的是否是同一个对象!)

理解了这一点,学习equals方法就简单多了!我们先来看看equals方法的原型,equals方法在Object类中
在这里插入图片描述
有没有发现,equals方法,返回正是刚刚所讲的内容,说明equals方法默认就是判断两个引用指向的是否是同一个对象!

注意: equals方法的返回值是boolean类型!

再来尝试运行这段代码:

public class Test1 {
    public static void main(String[] args) {
        Dog dog1 = new Dog("zhangsan",1);
        Dog dog2 = new Dog("zhangsan",1);
        System.out.println(dog1.equals(dog2));
    }
}

运行结果依旧是false


那么,问题来了,如果我们想通过比较两个对象的属性是否相等,如果相等,从逻辑上就说明他们就是相等的,该怎么办呢?

我们知道Object类是一切类的父类,而equals方法在Object类中,是不是就通过可以重写equals方法,从而达到自定义比较方式的目的!

下面就演示重写equals方法,规则:如果两个对象的属性完全一致,则返回true;否则放回false

public class Dog {
    private String name;
    private int age;

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public boolean equals(Object obj) {
        Dog tmp = (Dog)obj;
        return this.name.equals(tmp.name) && this.age == tmp.age;
    }
}
public class Test1 {
    public static void main(String[] args) {
        Dog dog1 = new Dog("zhangsan",1);
        Dog dog2 = new Dog("zhangsan",1);
        System.out.println(dog1.equals(dog2));
    }
}

这时运行代码,输出的就是true!

这时,有人可能就有疑问了,为什么名字比较要用equals方法,其实这里的equals方法并非Dog类中的equals方法,而是String类中的equals方法,用来判断两个字符串是否相等

String类中的equals方法原型:
在这里插入图片描述
注意: 对比String类中equals方法和Object类中equals方法的区别,Object类中的equals方法默认是判断两个对象的地址是否一致,如果想从逻辑上判断两个对象相等(即属性完全一致)就需要重写equals方法,而String类中equals方法直接就是从逻辑上判断两个字符串是否相同的(即判断两个字符串内容是否相同)
在这里插入图片描述

  • 第3行代码是判断两个字符串内容是否一致,因此会执行第4行代码
  • 第5行代码是判断两个字符串的地址是否一致,因此不会执行第6行代码

另外,编译器可以帮我们自动生成equals方法,第一步按住alt+insert
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
一路点下去,就可以生成equals方法,除此之外,还生成了hashCode方法
在这里插入图片描述

2. Comparable接口

通过重写equals方法,我们可以从逻辑上去判断两个对象是否相同。但是如果要去比较两个对象的大小,又该怎么去比较呢?对象的属性那么多,通过什么属性去比较呢?这时就需要讲到Comparable接口!

在这里插入图片描述
当我们在Student类后加上Comparable接口时,发现会报错,这是为什么呢?我们按住CTRL键,再鼠标左击Comparable,进入源码
在这里插入图片描述
我们发现,Comparable接口中还有个compareTo抽象方法,因此当我们在Student类要实现这个方法,除此之外,Comparable接口后还有个,这是泛型,我们需要比较什么类型的对象,就在实现接口时把T改成什么
在这里插入图片描述
由编译器自动生成实现compareTo方法的代码后:
在这里插入图片描述
我们就需要书写compareTo的方法体,那怎么书写呢?你想根据哪个对象的属性进行比较,就怎么书写。


比如,我现在想根据对象的年龄进行比较
在这里插入图片描述
下面进行测试:

public class Student implements Comparable<Student>{
    private String name;
    private int age;

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

    @Override
    public int compareTo(Student o) {
        return this.age-o.age;
    }
}
public class Test {
    public static void main(String[] args) {
        Student student1 = new Student("zhangsan", 18);
        Student student2 = new Student("lisi", 10);
        System.out.println(student1.compareTo(student2));
    }
}

运行结果:
在这里插入图片描述
返回的是一个正数,说明根据年龄比较,student1大于student2


再比如,我不想根据年龄比较了,我想根据姓名比较,这时就不能用简单的this.name-o.name了,因为name是一个String类型,不能通过这样的方式进行比较
在这里插入图片描述
方法里的compareTo指并不是在Student类中具体实现的这个compareTo,而是String类中的conpareTo,用于字符串的比较,它的底层是和C语言的strcmp是一样的,返回的是两个字母的ASCII 码的差值
String类中的compareTo方法


那么,问题来了,前面我们比较的只有两个对象,如果需要比较多个对象呢,改怎么办呢?这时候就需要用到数组来存放对象,用Arrays里的排序方法进行排序

当我们写下以下代码:

public class Student {
    private String name;
    private int age;

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Test {
    public static void main(String[] args) {
        Student[] students = new Student[]{
                new Student("zhangsan",18),
                new Student("lisi", 10),
                new Student("wangwu", 20)};
        System.out.println("排序前:" + Arrays.toString(students));
        Arrays.sort(students);
        System.out.println("排序后:" + Arrays.toString(students));
    }
}

当我们运行代码后,发现会出现异常
在这里插入图片描述
在这里插入图片描述

这里需要用到强转,而Student类并没有去实现Comparable接口,因此会导致强转失败!这就是异常所在!所以,我们需要在Student类中实现Comparable接口

public class Student implements Comparable<Student>{
    private String name;
    private int age;

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

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

    @Override
    public int compareTo(Student o) {
        return this.age-o.age;
    }
}

在这里插入图片描述

当我们再次去运行代码时,会发现排序后是根据年龄从小到大排序的,这和Student类中实现的compareTo难道有关系?答案是正确的

假如我们想按照年龄从大到小去排序,就做出如下更改
在这里插入图片描述
调换顺序即可,再次运行代码:
在这里插入图片描述


假如想根据年龄去比呢?

public class Student implements Comparable<Student>{
    private String name;
    private int age;

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

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

    @Override
    public int compareTo(Student o) {
        return this.name.compareTo(o.name);
    }
}
public class Test1 {
    public static void main(String[] args) {
        Student[] students = new Student[]{
                new Student("zhangsan",10),
                new Student("lisi", 18),
                new Student("wangwu", 9)};
        System.out.println("排序前:" + Arrays.toString(students));
        Arrays.sort(students);
        System.out.println("排序后:" + Arrays.toString(students));
    }
}

3. Comparator接口

在前面使用Comparable接口来实现对象的比较时,我们不难发现,这个比较方法并不灵活,只能固定地通过一种方式去比较,那么如果我们有的时候想通过年龄比较,有的时候想通过姓名比较,这个接口就无法实现了,这时地解决办法就是,换一个接口,用Comparator接口来实现!

这时,我们分别写两个类——NameComparator和AgeComparator,代表分别通过姓名比较和通过年龄比较。并且这两个类都要实现Comparator接口!

import java.util.Comparator;

public class NameComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.getName().compareTo(o2.getName());//姓名通过compareTo方法进行比较
    }
}
import java.util.Comparator;

public class AgeComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.getAge() - o2.getAge();
    }
}

注意: 实现Comparator接口需要重写的时compare方法,不是compareTo方法!实现Comparable接口重写的才是compareTo方法!

学生类如下:

public class Student {
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

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

测试一下代码:

import java.util.Arrays;

public class Test {
    public static void main(String[] args) {
        //两个对象的比较
        AgeComparator ageComparator = new AgeComparator();
        NameComparator nameComparator = new NameComparator();
        Student student1 = new Student("zhangsan", 20);
        Student student2 = new Student("lisi", 30);
        System.out.println("根据年龄比:" + ageComparator.compare(student1, student2));
        System.out.println("根据姓名比:" + nameComparator.compare(student1, student2));

        //一组对象的比较
        Student[] students = new Student[]{
                new Student("zhangsan", 10),
                new Student("lisi", 18),
                new Student("wangwu", 9)};
        System.out.println("排序前:" + Arrays.toString(students));
        Arrays.sort(students, ageComparator);
        System.out.println("根据年龄排序后:" + Arrays.toString(students));
        Arrays.sort(students, nameComparator);
        System.out.println("根据姓名排序后:" + Arrays.toString(students));
    }
}

分析两个对象的比较:
在这里插入图片描述
这里,我们创建了一个AgeComparator和一个NameComparator对象,用来表示是通过年龄比较还是通过姓名比较。注意最后两个Student类对象的比较方法

分析一组对象的比较:
在这里插入图片描述
我们发现,在用Arrays.sort排序时,里面还加上了一个ageComparator对象(或NameComparator对象),通过这个对象,可以控制通过什么方式去比较,当使用Arrays对数组进行排序时,就会调用ageComparator(或NameComparator)里的compare方法依次将数组里的对象进行比较

最后,运行结果:
在这里插入图片描述


请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值