Java - 对象的比较

红太狼 2024-03-26 17:40 172阅读 0赞

一、问题提出

前面讲了优先级队列,优先级队列在插入元素时有个要求:插入的元素不能是null或者元素之间必须要能够进行比较,为了简单起见,我们只是插入了Integer类型,
在这里插入图片描述
那优先级队列中能否插入自定义类型对象呢?
在这里插入图片描述

  1. class Card{
  2. public int rank; // 数值
  3. public String suit; // 花色
  4. public Card(int rank, String suit) {
  5. this.rank = rank;
  6. this.suit = suit;
  7. }
  8. }
  9. public class TestDome {
  10. public static void main(String[] args) {
  11. PriorityQueue<Card> priorityQueue = new PriorityQueue<>();
  12. priorityQueue.offer(new Card(5,"♣"));
  13. priorityQueue.offer(null);
  14. System.out.println(priorityQueue);
  15. }
  16. }

优先级队列底层使用堆,而向堆中插入元素时,为了满足堆的性质,必须要进行元素的比较,而此时Card是没有办
法直接进行比较的,因此抛出异常。
通过实践,我们发现在没有指定 某种排序规则时,是无法将自定义类型入队的。
至于 Comparable,可以参考这篇文章javaSE - 三个常用的接口(Comparable,Comparator,Cloneable)

回顾

关于 对象的比较来说:
1、equals 方法,比较的是对象,如果比较的两个对象不相同:返回 false,反之,返回true
2、比较大小
我们的 Comparable 和 compareTo 使用比较大小的。指定比较的方式

我们要去堆Card 这种类型的对象进行比较,那就要实现Comparable或者Comparator,然后指定比较的方式,进行比较
Card 这个类里面有rank 和 suit 两个属性,是根据rank 或者 suit 进行比较

方法一、实现Comparable 接口

例如:使用rank 进行比较
方法一: 实现Comparable 接口 【注意!实现结结构,是要重写接口内部的抽象方法的】

  1. class Card implements Comparable<Card>{
  2. public int rank; // 数值
  3. public String suit; // 花色
  4. public Card(int rank, String suit) {
  5. this.rank = rank;
  6. this.suit = suit;
  7. }
  8. @Override
  9. public int compareTo(Card o) {
  10. return this.rank - o.rank;
  11. }
  12. @Override
  13. public String toString() {
  14. return "Card{" +
  15. "rank=" + rank +
  16. ", suit='" + suit + '\'' +
  17. '}';
  18. }
  19. }
  20. public class TestDome {
  21. public static void main(String[] args) {
  22. PriorityQueue<Card> priorityQueue = new PriorityQueue<>();
  23. priorityQueue.offer(new Card(5,"♣"));
  24. priorityQueue.offer(new Card(2,"♥"));
  25. System.out.println(priorityQueue);
  26. }
  27. }

这里是引用

那么,问题来了。我们也没有看见 优先级队列调用 compareTo 啊?
这里我们就需要去看一下,PriorityQueue 的 原码。
既然,自定义类型的数据能放的进去,而且 其结果 是有序的。
那么,说明 offer 在添加 自定义元素时,肯定是比较了的。
所以,我们从 offer 入手。
在这里插入图片描述

方法二:创建一个比较器(类),用来实现 Comparator 接口。通过这个类,来确定比较的规则

方法一对类的倾入性太强了,也就是说,在Card这个类里面实现比较的方式,那这个比较的方式就不能改了,但是如果哪天我们不根据rank进行比较,先要通过suit进行比较,那这个累计就不能实现比较了
在这里插入图片描述
所以我们应该将比较的方式单独实现一个类,将不同的比较方式提出来,例如:
在这里插入图片描述

利用匿名内部类实现上面的Comparator ,就是利用匿名内部类试下比较器
在这里插入图片描述
使用 lambda 表达式 - 与上一种方法是等价的。
在这里插入图片描述

二、元素的比较

2.1、基本类型的比较

在Java中,基本类型的对象可以直接比较大小。

  1. public static void main(String[] args) {
  2. int a = 10;
  3. int b = 20;
  4. System.out.println(a > b);
  5. System.out.println(a < b);
  6. System.out.println(a == b);
  7. char c1 = 'A';
  8. char c2 = 'B';
  9. System.out.println(c1 > c2);
  10. System.out.println(c1 < c2);
  11. System.out.println(c1 == c2);
  12. boolean b1 = true;
  13. boolean b2 = false;
  14. System.out.println(b1 == b2);
  15. System.out.println(b1 != b2);
  16. }

在这里插入图片描述

2.2、对象的比较

  1. class Card {
  2. public int rank; // 数值
  3. public String suit; // 花色
  4. public Card(int rank, String suit) {
  5. this.rank = rank;
  6. this.suit = suit;
  7. }
  8. }
  9. public class TestPriorityQueue {
  10. public static void main(String[] args) {
  11. Card c1 = new Card(1, "♠");
  12. Card c2 = new Card(2, "♠");
  13. Card c3 = c1;
  14. //System.out.println(c1 > c2); // 编译报错
  15. System.out.println(c1 == c2); // 编译成功 ----> 打印false,因为c1和c2指向的是不同对象
  16. //System.out.println(c1 < c2); // 编译报错
  17. System.out.println(c1 == c3); // 编译成功 ----> 打印true,因为c1和c3指向的是同一个对象
  18. }
  19. }

c1、c2和c3分别是Card类型的引用变量,上述代码在比较编译时:
c1 > c2 编译失败
c1== c2 编译成功
c1 < c2 编译失败
从编译结果可以看出,Java中引用类型的变量不能直接按照 > 或者 < 方式进行比较。 那为什么可以比较?
因为:对于用户实现自定义类型,都默认继承自Object类,而Object类中提供了equal方法,而默认情况下调
用的就是equal方法,但是该方法的比较规则是:没有比较引用变量引用对象的内容,而是直接比较引用变量的地
址,但有些情况下该种比较就不符合题意。

我们也可以重写equal方法:

  1. class Card {
  2. public int rank; // 数值
  3. public String suit; // 花色
  4. public Card(int rank, String suit) {
  5. this.rank = rank;
  6. this.suit = suit;
  7. }
  8. @Override
  9. public String toString() {
  10. return "Card{" +
  11. "rank=" + rank +
  12. ", suit='" + suit + '\'' +
  13. '}';
  14. }
  15. @Override
  16. public boolean equals(Object o) {
  17. if (this == o) return true;
  18. if (o == null || getClass() != o.getClass()) return false;
  19. Card card = (Card) o;
  20. return rank == card.rank && Objects.equals(suit, card.suit);
  21. }
  22. @Override
  23. public int hashCode() {
  24. return Objects.hash(rank, suit);
  25. }
  26. }
  27. public class TestDome {
  28. public static void main(String[] args) {
  29. Card card1 = new Card(5,"♥");
  30. Card card2 = new Card(5,"♥");
  31. System.out.println(card1.equals(card2));
  32. }
  33. }

在这里插入图片描述
注意: 一般覆写 equals 的套路就是上面演示的

  1. 如果指向同一个对象,返回 true
  2. 如果传入的为 null,返回 false
  3. 如果传入的对象类型不是 Card,返回 false
  4. 按照类的实现目标完成比较,例如这里只要花色和数值一样,就认为是相同的牌
  5. 注意下调用其他引用类型的比较也需要 equals,例如这里的 suit 的比较
    覆写基类equal的方式虽然可以比较,但缺陷是:equal只能按照相等进行比较,不能按照大于、小于的方式进行
    比较。

2.3、三种方式对比

在这里插入图片描述

发表评论

表情:
评论列表 (有 0 条评论,172人围观)

还没有评论,来说两句吧...

相关阅读

    相关 java对象比较

    本文章主要介绍了Java对象的比较,首先我们介绍了元素和对象之间分别是如何比较的,然后我们讲解了继承Comparable接口进行比较,最后讲解了什么是PriorithQu...

    相关 Java - 对象比较

    一、问题提出 > 前面讲了优先级队列,优先级队列在插入元素时有个要求:插入的元素不能是null或者元素之间必须要能够进行比较,为了简单起见,我们只是插入了Integer类

    相关 Java对象比较

    一 点睛 在Java中,有两种方式可用于对象间的比较: 利用"=="运算符:用于比较两个对象的内存地址值(引用值)是否相等。 利用equals()方法:用于比较