java对象的比较(Comparble、equals、Comparator)

java对象的比较

1. PriorityQueue中插入对象

优先级队列在插入元素时有个要求:插入的元素不能是null或者元素之间必须要能够进行比较,那如果我们插入的是自定义类型对象该如何比较呢?

比如说我们定义了一个学生类

class Student{
    public int age;
    public String name;
}
public class Test {
    public static void main(String[] args) {
        Student student1 = new Student();
        student1.name = "ahangsan";
        student1.age = 10;

        Student student2 = new Student();
        student2.name = "zhangsan";
        student2.age = 9;
    }
}

要想将这两个对象放入优先级队列中,直接放是不可能的,通过PriorityQueue的offer方法的原码可知,如果没有comparator,对象会被强转成Comparable类型,所以我们可以有两种方式:

一是构建comparator;

二是实现Comparable接口。

2.对象的比较

  1. 基于Comparble接口类的比较

源码如下:

public interface Comparable<E> {
    // 返回值:
    // < 0: 表示 this 指向的对象小于 o 指向的对象
    // == 0: 表示 this 指向的对象等于 o 指向的对象
    // > 0: 表示 this 指向的对象大于 o 指向的对象
    int compareTo(E o);
}

对用用户自定义类型,如果要想按照大小与方式进行比较时:在定义类时,实现Comparble接口即可,然后在类中重写compareTo方法。

class Student implements Comparable<Student> {
    public int age;
    public String name;
    @Override
    public int compareTo(Student o) {
        //比较对象的大小关系,返回只有正负数和0
        return this.age - o.age;
    }
}
public class Test {
    public static void main(String[] args) {
        Student student1 = new Student();
        student1.name = "ahangsan";
        student1.age = 10;

        Student student2 = new Student();
        student2.name = "zhangsan";
        student2.age = 9;

        System.out.println(student1.compareTo(student2));
    }
}

上面的代码可以根据年龄来进行比较,但是这种方法有个弊端,如果我想根据姓名进行比较,我还要改写我的compareTo方法,这样一来,我原先使用年龄比较的代码就不能用了。

2. 覆写基类的equals

class Student {
    public int age;
    public String name;
    @Override
    public boolean equals(Object o) {
        //判断是否相等,返回true或者false
        if (this == o) return true;//判断是否是自己和自己比较
        if (o == null || getClass() != o.getClass()) return false;//判断对象是否为空,或者o是不是Student的子类
        Student student = (Student) o;
        return age == student.age && Objects.equals(name, student.name);//判断年龄,姓名是否一样
        //注意基本类型可以直接比较,但引用类型最好调用其equal方法
    }
}
public class Test {
    public static void main(String[] args) {
        Student student1 = new Student();
        student1.name = "ahangsan";
        student1.age = 10;

        Student student2 = new Student();
        student2.name = "zhangsan";
        student2.age = 9;
        
        Student student2 = new Student();
        student2.name = "zhangsan";
        student2.age = 9;

        System.out.println(student1.equals(student2));
        System.out.println(student3.equals(student2));
    }
}

输出结果是:

false

true

这个方法只能用作对象之间的比较,比较是否相等,而不能比较出谁大谁小,所以不能用在PriorityQueue的插入元素中。

如果没有重写equals方法,那会自动调用object类的equals方法,此时和直接写student1 == student2没有区别,比较的还是对象的地址。

覆写基类equal的方式虽然可以比较,但缺陷是:equal只能按照相等进行比较,不能按照大于、小于的方式进行比较

  1. 基于比较器比较
  • 用户自定义比较器类,实现Comparator接口
public interface Comparator<T> {
    // 返回值:
    // < 0: 表示 o1 指向的对象小于 o2 指向的对象
    // == 0: 表示 o1 指向的对象等于 o2 指向的对象
    // > 0: 表示 o1 指向的对象等于 o2 指向的对象
    int compare(T o1, T o2);
}

注意区分Comparator和Comparable

  • 覆写Comparator中的compare方法
class Student {
    public int age;
    public String name;
    }
}
//年龄比较器
class AgeComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.age - o2.age;
    }
}
//姓名比较器
class NameComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.name.compareTo(o2.name);//调用引用类型String的方法
    }
}
public class Test {
    public static void main(String[] args[]) {
        Student student1 = new Student();
        student1.name = "zhangsan";
        student1.age = 10;

        Student student2 = new Student();
        student2.name = "zhangsan";
        student2.age = 9;
        
        AgeComparator ageComparator = new AgeComparator();
        System.out.println(ageComparator.compare(student1,student2));
        NameComparator nameComparator = new NameComparator();
        System.out.println(nameComparator.compare(student1,student2));
    }
}

输出:

1 // student1年龄大于student2

0 // student1姓名和student2相同

三种方法的对比

覆写的方法说明
Object.equals因为所有类都是继承自 Object 的,所以直接覆写即可,不过只能比较相等与否
Comparable.compareTo需要手动实现接口,侵入性比较强,但一旦实现,每次用该类都有顺序,属于内部顺序
Comparator.compare需要实现一个比较器对象,对待比较类的侵入性弱,但对算法代码实现侵入性强

3. PriorityQueue的比较方式

我们了解了对象的比较方式,现在就回过来思考PriorityQueue内部的元素是如何采用Comparble和Comparator比较的呢?

// JDK中PriorityQueue的实现:
public class PriorityQueue<E> extends AbstractQueue<E> implements java.io.Serializable {
    // ...
    // 默认容量
    private static final int DEFAULT_INITIAL_CAPACITY = 11;
    // 内部定义的比较器对象,用来接收用户实例化PriorityQueue对象时提供的比较器对象
    private final Comparator<? super E> comparator;
    // 用户如果没有提供比较器对象,使用默认的内部比较,将comparator置为null
    public PriorityQueue() {
        this(DEFAULT_INITIAL_CAPACITY, null);
    }
    // 如果用户提供了比较器,采用用户提供的比较器进行比较
    public PriorityQueue(int initialCapacity, Comparator<? super E> comparator) {
        // Note: This restriction of at least one is not actually needed,
        // but continues for 1.5 compatibility
        if (initialCapacity < 1)
        	throw new IllegalArgumentException();
        this.queue = new Object[initialCapacity];
        this.comparator = comparator;
    }
    // ...
    // 向上调整:
    // 如果用户没有提供比较器对象,采用Comparable进行比较
    // 否则使用用户提供的比较器对象进行比较
    private void siftUp(int k, E x) {
        if (comparator != null)
        	siftUpUsingComparator(k, x);
        else
        	siftUpComparable(k, x);
    }
    // 使用Comparable
    @SuppressWarnings("unchecked")
    private void siftUpComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>) x;
        while (k > 0) {
            int parent = (k - 1) >>> 1;
            Object e = queue[parent];
            if (key.compareTo((E) e) >= 0)
            	break;
            queue[k] = e;
            k = parent;
        }
        queue[k] = key;
    }
    // 使用用户提供的比较器对象进行比较
    @SuppressWarnings("unchecked")
    private void siftUpUsingComparator(int k, E x) {
        while (k > 0) {
            int parent = (k - 1) >>> 1;
            Object e = queue[parent];
            if (comparator.compare(x, (E) e) >= 0)
            	break;
            queue[k] = e;
            k = parent;
        }
        queue[k] = x;
    }
}
  1. Comparble是默认的内部比较方式,如果用户插入自定义类型对象时,该类对象必须要实现Comparble接口,并覆写compareTo方法。

    class Student implements Comparable<Student> {
        public int age;
        public String name;
        @Override
        public int compareTo(Student o) {
            //比较对象的大小关系,返回只有正负数和0
            return this.age - o.age;
        }
    }
    public class Test {
        public static void main(String[] args) {
            Student student1 = new Student();
            student1.name = "ahangsan";
            student1.age = 10;
    
            Student student2 = new Student();
            student2.name = "zhangsan";
            student2.age = 9;
    
            PriorityQueue<Student> priorityQueue = new PriorityQueue<>();
            priorityQueue.offer(student1);
            priorityQueue.offer(student2);
            Student ret = priorityQueue.poll();
            //ret = "zhangsan" ,9
        }
    }
    
  2. 用户也可以选择使用比较器对象,如果用户插入自定义类型对象时,必须要提供一个比较器类,让该类实现Comparator接口,并覆写compare方法

    class Student implements Comparable<Student> {
        public int age;
        public String name;
        @Override
        public int compareTo(Student o) {
            //比较对象的大小关系,返回只有正负数和0
            return this.age - o.age;
        }
    }
    class AgeComparator implements Comparator<Student> {
        @Override
        public int compare(Student o1, Student o2) {
            return o1.age - o2.age;
        }
    }
    class NameComparator implements Comparator<Student> {
        @Override
        public int compare(Student o1, Student o2) {
            return o1.name.compareTo(o2.name);
        }
    }
    public class Test {
        public static void main(String[] args) {
            Student student1 = new Student();
            student1.name = "ahangsan";
            student1.age = 10;
    
            Student student2 = new Student();
            student2.name = "zhangsan";
            student2.age = 9;
    
            AgeComparator ageComparator = new AgeComparator();
            PriorityQueue<Student> priorityQueue1 = new PriorityQueue<>(ageComparator);
            //PriorityQueue<Student> priorityQueue1 = new PriorityQueue<>(new AgeComparator());
            priorityQueue1.offer(student1);
            priorityQueue1.offer(student2);
            System.out.println(priorityQueue1.poll());
            //"zhangsan" ,9
    
            NameComparator nameComparator = new NameComparator();
            PriorityQueue<Student> priorityQueue2 = new PriorityQueue<>(nameComparator);
            //PriorityQueue<Student> priorityQueue2 = new PriorityQueue<>(new NameComparator());
            priorityQueue2.offer(student1);
            priorityQueue2.offer(student2);
            System.out.println(priorityQueue2.poll());
            //"ahangsan" ,10
        }
    }
    

这两种方式可以同时存在,互不影响。

  • 14
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

是布谷阿

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

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

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

打赏作者

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

抵扣说明:

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

余额充值