java中对象的比较equal、Comparble、Comparator的区别

目录
  • 关于对象值相等的比较
    • 三种比较风格
    • 覆写基类的equal
  • 关于对象值大于、等于、小于的比较–基于自然顺序(按照<小于号的形式)
    • 基于Comparble接口类的比较
  • 关于对象值大于、等于、小于的比较-- 基于比较器比较
    • 基于Comparator接口类的比较
  • 三种比较方式对比

关于对象值相等的比较

三种比较风格

  • 比较身份:==,通过等号来比较身份
  • 比较值:通过使用equals方法,它是Object这个祖宗类所提供的的一个方法,也可以自己写个类,重写equals,自定制比较值的规则。
  • 这里的equals需要通过用户手动重写才能够按照值比较,换句话说就是你要是想比较值的话,就要约定好你到底按照什么样的规则来进行比较,因为如果是比较身份的话,编译器比较清楚,可以比较引用的地址,但如果是比较值的话,你一个类中有很多属性,到底按哪些规则来比较,谁重要谁不重要,编译器自己也不知道,这就需要用户根据业务场景来指定按照哪些字段进行比较。
  • 如果equals没有手动重写,默认执行的就是Object版本中的equals,此时的比较规则也是在比较身份。
  • 比较类型: 通过instanceof,在类型转型之前先比较一下,看看当前引用的真实类型到底是什么,以免出现类型转换异常。

验证== 和 equas在比较风格上的区别

代码实现:按照身份来比较

package java2021_1018;
//写一个辅助的类Card(扑克牌-之前写过)
class Card{
    //按照点数来比较
    public String rank;//点数
    public String suit;//花色
    //提供构造方法
    public Card(String rank, String suit) {
        this.rank = rank;
        this.suit = suit;
    }
}
public class TestCompare {
    public static void main(String[] args) {
        //写一个类方法,在类方法中构造出这两张牌
        Card a=new Card("3","♠");//第一张牌:a的点数为1,花色为黑桃
        Card b=new Card("3","♠");//第二张牌:b的点数为1,花色为黑桃
        Card c=a;
        //此时已经有了三张牌,但是这三张牌来的方式并不一样,a是通过new一个Card对象,
        //b也是是通过new一个Card对象,而c是相当于和直接a指向同一个card对象

        //《使用 == 等号,进行比较》
        System.out.println("==============使用 == 等号,进行比较的结果==============");
        System.out.println(a == c);//结果为true
        System.out.println(a == b);//结果为false
        //第二个结果为true是因为a和b分别new了两个对象,这两个对象的身份是不相等的,所以执行 == 比较就是false的情况
        //《使用equals进行比较》
        System.out.println("==============使用equals进行比较的结果==============");
        System.out.println(a.equals(c));//结果为true
        System.out.println(a.equals(b));//结果为false
        /*此时发现当前使用的equals和 == 结果一样,没什么区别,这说明当前这个equals你没有对他进行重写的时候,它就仍然是按照身份的方式进行比较的;
        * 所以,如果equals没有手动重写,默认执行的就是Object版本中的equals,此时的比较规则也是在比较身份。*/
    }
}

打印结果:

覆写基类的equal

给equals加上一个重写,使它变成比较值(内容)的方法

代码实现:按照值来比较

package java2021_1018;

class Card{
    public String rank;//点数
    public String suit;//花色
    public Card(String rank, String suit) {
        this.rank = rank;
        this.suit = suit;
    }

    @Override
    public boolean equals(Object obj) {//参数:obj,类型:Object
        //按照值来比较this和obj
        //1.考虑自己和自己比较的情况
        if(this == obj){ //先看看比较的这两个对象是不是同一个对象,如果是,就返回true
            return true;//因为两个对象(引用)如果身份相同的话,那么值也肯定是相同的
        }
        //2.考虑obj为null的情况,认为结果为false, 避免出现空引用异常。 因为this是不可能为null的,如果它是null,就无法调用equals方法了
        //所以如果obj为null,this不为null,那就返回false,有了这样的一个条件判断,就可以保证在后面这个obj == null逻辑执行的时候,就不会出现空引用异常了
        if(obj == null){
            return false;
        }
        //3、考虑类型不匹配的情况,即考虑obj这个类型是不是当前的Card类型,如果equals里传了一个其他参数类型进来,此时两个类型不同是无法比较相等的,所以需要判断一下哦
        if (!(obj instanceof Card)){//如果obj这个参数不是Card这个类型的话,就返回false
            return false;
            //同时类型转换也带有类型转换失败的风险,所以在使用之前也要先确认好类型是否匹配
        }
        //4.真正的比较内容
        Card other = (Card)obj;//此时的参数类型是Object类型,所以需要先对obj进行一个类型的强转,并赋值给一个变量other
        //再去比较判断 点数或花色 或 点数和花色 是否相等
        return this.rank.equals(other.rank) && this.suit.equals(other.suit);
    }
    //这相当于是一个标准的重写equals的一个模板,以后再写其他的一些自己的比较方法的时候也要按照这种思路一步步往下考虑
}
public class TestCompare1 {
    public static void main(String[] args) {
        Card a=new Card("3","♠");//第一张牌:a的点数为1,花色为黑桃
        Card b=new Card("3","♠");//第二张牌:b的点数为1,花色为黑桃
        Card c=a;
        System.out.println("==============使用 == 等号,进行比较的结果==============");
        System.out.println(a == c);//结果为true
        System.out.println(a == b);//结果为true,因为a和b分别new了两个对象,这两个对象的身份是不相等的,所以执行 == 比较就是false的情况
        System.out.println("==============使用equals进行比较的结果==============");
        System.out.println(a.equals(c));//结果为true
        System.out.println(a.equals(b));//结果为true
    }
}

打印结果:

注意:一般覆写 equals 的套路就是上面演示的

在实现equals中所需考虑的几个步骤,及涉及到的细节,在实现其他类的equals时也基本上就是代码中所考虑的这几个操作(套路)。

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

关于对象值大于、等于、小于的比较–基于自然顺序(按照<小于号的形式)

基于Comparble接口类的比较

Comparble这个接口相当于就是重新定义小于这个操作

下面通过代码来体会一下Comparble这个接口的作用,还是基于Card这个类来进行比较,如果想要使用Comparble这个接口的话,就需要让Card实现一个Comparble,由于Comparble是一个带泛型的接口,于是就需要给它写一个泛型参数,但也不是非写不可。

代码实现:基于Comparble接口类的比较

package java2021_1018;

class Card  implements Comparable<Card>{//实现一个 Comparable的接口,由于Comparble是一个带泛型的接口,于是就需要给它写一个泛型参数
    //按照点数来比较
    public String rank;//点数
    public String suit;//花色
    //提供构造方法
    public Card(String rank, String suit) {
        this.rank = rank;
        this.suit = suit;
    }
//重写compareTo方法
    @Override
    public int compareTo(Card o) {
        if(o == null){//如果o传过来的参数是一个空引用,就认为this比null要大
            //一般也认为null的值比较小
            return 1;
        }
        //点数的取值:是2~10的一系列整数,和J  Q  K  A;如果点数值在2~10的话,直接返回成整数,如果点数值是J  Q  K  A的话,就手动把这四个点数设置成11,12,13,14然后把值算出来之后再去分别比较大小
        int rank1 = this.getValue();//this的值
        int rank2 = o.getValue();//o的值
        return rank1-rank2;//返回二者的差值,返回的值参考compareTo的语法规则
    }
//写一个获取值的方法,
    private int getValue() {
        //通过这个方法把String 类型的rank(J  Q  K  A)变成数字点数11,12,13,14
        int value = 0;
        if("J".equals(rank)){
            value = 11;
        }else if("Q".equals(rank)){
            value = 12;
        }else if("K".equals(rank)){
            value = 13;
        }else if ("A".equals(rank)){
            value = 14;
        }else {
            value = Integer.parseInt(rank);//把字符串转成数字
            //单独处理J  Q  K  A,然后其他的2~10直接使用Integer.parseInt,把字符串转换成整数。
        }
        return value;
    }

public class TestCompare1 {
    public static void main(String[] args) {
        //写一个类方法,在类方法中构造出这两张牌
        Card a=new Card("3","♠");//第一张牌:a的点数为1,花色为黑桃
        Card b=new Card("2","♦");//第二张牌:b的点数为1,花色为黑桃
        Card c=a;
        //调用compareTo比较方法
        System.out.println(a.compareTo(b));
        System.out.println(a.compareTo(c));
    }
}

compareTo方法的语法规则

  • 如果认为this 比 o 小,就返回一个<0的整数
  • 如果认为this 比 o 大,就返回一个>0的整数
  • 如果认为this 比 o 相等,就返回0

不同的点数比较后所打印出来的结果如下图:

关于对象值大于、等于、小于的比较-- 基于比较器比较

基于Comparator接口类的比较

Comparator也是一个接口,想要使用它也要让你的类去实现这个接口,同时去重写一个compare方法,但是不同的是compare方法里面有两个参数,

Comparable和Comparator,他俩的区别在于Comparator定义出的比较器和原来的类不是一个耦合在一起的关系

即使用Comparable的时候,你必须让要比较的类实现Comparable接口,换句话说,就是需要修改这个类的代码,比如刚才待比较类是Card,如果要是用Comparable这种实现方式的时候,必须得修改Card的源码。

而使用Comparator的时候,你是重新创建一个新的类实现Comparator接口,不需要修改待比较类的代码,比如刚才待比较类是Card,如果要是用Comparator这种实现方式的时候,就不用改Card的源码

所以说使用Comparable这种实现方式的时候,它的耦合性就更强一些,但这是我们不愿意看到的。

代码实现:基于Comparator接口类的比较

package java2021_1018;

import java.util.Comparator;

//写一个辅助的类Card(扑克牌-之前写过)
class Card {
    //按照点数来比较
    public String rank;//点数
    public String suit;//花色
    //提供构造方法

    public Card(String rank, String suit) {
        this.rank = rank;
        this.suit = suit;
    }
//写一个获取值的方法,
   public int getValue() {
        //通过这个方法把String 类型的rank(J  Q  K  A)变成数字点数11,12,13,14
        int value = 0;
        if("J".equals(rank)){
            value = 11;
        }else if("Q".equals(rank)){
            value = 12;
        }else if("K".equals(rank)){
            value = 13;
        }else if ("A".equals(rank)){
            value = 14;
        }else {
            value = Integer.parseInt(rank);//把字符串转成数字
            //单独处理J  Q  K  A,然后其他的2~10直接使用Integer.parseInt,把字符串转换成整数。
        }
        return value;
    }
}z
//写一个CardComparator类
class CatdComparator implements Comparator<Card>{//这里指定泛型参数就是针对谁比较就写谁
//实现一个compare方法
    @Override
    public int compare(Card o1, Card o2) {//compare方法里面有两个参数,类型都是Card类型
        //判断特殊情况
        if(o1 == o2){//如果o1 和 o2身份相等
            return 0;
        }
        //判断o1 、 o2是不是null的情况
        if(o1 == null){
            return -1;
        }
        if(o2 == null){
            return 1;
        }
        //比较值
        int value1 = o1.getValue();
        int value2 = o2.getValue();
        return value1-value2;
    }
}
public class TestCompare1 {
    public static void main(String[] args) {
        //写一个类方法,在类方法中构造出这两张牌
        Card a=new Card("3","♠");//第一张牌:a的点数为1,花色为黑桃
        Card b=new Card("K","♠");//第二张牌:b的点数为1,花色为黑桃
        Card c=a;
 //Comparator的使用:先创建一个Comparator的实例
        CatdComparator comparator = new CatdComparator();
        System.out.println(comparator.compare(a,b));
    }
}

疑问:为什么有了Comparable还需要有一个Comparator呢?

1、因为Comparable使用的时候必须要修改待比较类的代码,实际开发中不是所有的类都能修改源码,(如果这个类是库或者是其他组的人提供的此时就不能随便改人家的代码,只能改自己的代码)。

2、Comparable只能定义一种比较规则,Comparator可以定义多种比较规则(即可以实现多个Comparator类)。如:代码中的CardComparator类,定义多份之后就可以有多种不同的比较规则了,然后就可以在不同的比较规则里面分别针对当前的场景来自定制按照什么样的方式来比较了。

三种比较方式对比

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

到此这篇关于java中对象的比较equal、Comparble、Comparator的区别的文章就介绍到这了,更多相关java equal、Comparble、Comparator内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java中对象的比较操作实例分析

    本文实例讲述了Java中对象的比较操作.分享给大家供大家参考,具体如下: 一 点睛 在Java中,有两种方式可用于对象间的比较: 利用"=="运算符:用于比较两个对象的内存地址值(引用值)是否相等. 利用equals()方法:用于比较两个对象的内容是否一致. 二 "=="运算符的比较 1 代码 public class CompareObject1 { public static void main( String[] args ) { String str1 =

  • Java比较两个对象中全部属性值是否相等的方法

    例如下述Java类: import java.io.Serializable; import java.util.List; public class Bean_Topology implements Serializable { private static final long serialVersionUID = 1L; public static long getSerialversionuid() { return serialVersionUID; } private Long to

  • Java各种比较对象的方式的对比总结

    一.==和!=操作符 让我们从==和!=开始可以分别判断两个Java对象是否相同的操作符. 1.1 原始类型(Primitives) 对于原始类型,相同意味着具有相等的值: assertThat(1 == 1).isTrue(); 感谢自动拆箱,在将原语值与其包装类型对应值进行比较时,也可以这样做: Integer a = new Integer(1); assertThat(1 == a).isTrue(); 如果两个整数的值不同,==运算符将返回false,而!=运算符将返回true. 1.

  • Java中实现Comparable和Comparator对象比较

    当需要排序的集合或数组不是单纯的数字型时,通常可以使用Comparator或Comparable,以简单的方式实现对象排序或自定义排序. A comparison function, which imposes a total ordering on some collection of objects. Comparators can be passed to a sort method (such as Collections.sort or Arrays.sort) to allow pr

  • Java比较对象大小两种常用方法

    引入原因: Java中的对象,正常情况下,只能进行比较:== 或!= ,不能使用 < 或 > ,但是在开发时需要用到比较对象的大小 1.Comparable接口的使用(自然排序) 1.像String .包装类等实现了Comparable接口,重写了compareTo()方法,给出了比较两个对象大小的方法 2.像String .包装类等重写了compareTo()方法后,默认执行了从小到大的排序 3.重写compareTo()的规则: 如果当前对象this大于形参对象obj,则返回正整数,如果当

  • 浅谈java对象的比较

    目录 1.元素的比较 2.类的比较 3.比较方法 3.1 重写equals方法 3.2 基于Comparble接口类的比较 3.3 基于比较器比较基于比较器比较:Comparator接口 3.4 三种比较方式的对比 1.元素的比较 在java中,基本类型的对象可以直接比较大小. public static void main(String[] args) { int a=12; int b=55; System.out.println(a > b); System.out.println(a =

  • java中对象的比较equal、Comparble、Comparator的区别

    目录 关于对象值相等的比较 三种比较风格 覆写基类的equal 关于对象值大于.等于.小于的比较–基于自然顺序(按照<小于号的形式) 基于Comparble接口类的比较 关于对象值大于.等于.小于的比较-- 基于比较器比较 基于Comparator接口类的比较 三种比较方式对比 关于对象值相等的比较 三种比较风格 比较身份:==,通过等号来比较身份 比较值:通过使用equals方法,它是Object这个祖宗类所提供的的一个方法,也可以自己写个类,重写equals,自定制比较值的规则. 这里的eq

  • java对象对比之comparable和comparator的区别

    一.元素的比较 1.1 基本类型的比较 java中的基本类型的对象是可以进行比较的 如 public static void main(String[] args){ int a = 10; int b = 20; System.out.println(a>b); System.out.println(a==b); System.out.println(a<b); char c1 = 'A'; char c2 = 'B'; System.out.println(c1>c2); Syste

  • Java中对象的深复制(深克隆)和浅复制(浅克隆)介绍

    1.浅复制与深复制概念 ⑴浅复制(浅克隆) 被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象. ⑵深复制(深克隆) 被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量.那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象.换言之,深复制把要复制的对象所引用的对象都复制了一遍. 2.Java的clone()方法 ⑴clone方法将对象复制了一份并返回

  • Java中对象序列化与反序列化详解

    本文实例讲述了Java中对象序列化与反序列化.分享给大家供大家参考.具体如下: 一.简介 对象序列化(Serializable)是指将对象转换为字节序列的过程,而反序列化则是根据字节序列恢复对象的过程. 序列化一般用于以下场景: 1.永久性保存对象,保存对象的字节序列到本地文件中: 2.通过序列化对象在网络中传递对象: 3.通过序列化在进程间传递对象. 对象所属的类必须实现Serializable或是Externalizable接口才能被序列化.对实现了Serializable接口的类,其序列化

  • Java中对象的序列化详解及实例

     Java中对象的序列化详解及实例 把java对象转化为字节序列的过程称为对象的序列化. 把字节序列恢复为java对象的过程称为对象的反序列化. 对象序列化的用途: 1.把对象的字节序列永久的保存到硬盘上,通常存放在一个文件中 2.在网络上传送对象的字节序列化 void writeObject(Object obj) 方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中(将指定的对象写入 ObjectOutputStream.) void readObject()方法 从

  • 详解Java中对象序列化与反序列化

    序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程.一般将一个对象存储至一个储存媒介,例如档案或是记亿体缓冲等.在网络传输过程中,可以是字节或是XML等格式.而字节的或XML编码格式可以还原完全相等的对象.这个相反的过程又称为反序列化. Java对象的序列化与反序列化 在Java中,我们可以通过多种方式来创建对象,并且只要对象没有被回收我们都可以复用该对象.但是,我们创建出来的这些Java对象都是存在于JVM的堆内存中的.只有JVM处于运行状态的时候,这些对

  • 浅析Java中对象的创建与对象的数据类型转换

    Java:对象创建和初始化过程 1.Java中的数据类型     Java中有3个数据类型:基本数据类型(在Java中,boolean.byte.short.int.long.char.float.double这八种是基本数据类型).引用类型和null类型.其中,引用类型包括类类型(含数组).接口类型.     下列语句声明了一些变量: int k ; A a; //a是A数据类型的对象变量名. B b1,b2,-,b10000;// 假定B是抽象类或接口. String s; 注意:从数据类型

  • Java中对象与C++中对象的放置安排的对比

    Java中对象与C++中对象的放置安排的对比 概要: Java中,所有的对象都存放在堆(Heap,一种通用的内存池)中:而对象的引用是存放在堆栈(Stack)中的. 我们可以通过String直接声明的字符串与new String声明出来的字符串使用equals()和"=="进行的比较,从而理解对象和引用的关系及它们的存储位置. 堆栈是一种快速有效的分配存储方法,仅次于寄存器.创建程序时,Java系统必须知道存储在堆栈内所有项的确切生命周期,以便上下移动堆栈指针. 堆不同于堆栈的好处是:

  • Java中对象的销毁方法分析

    本文较为详细的分析了Java中对象的销毁方法.分享给大家供大家参考.具体分析如下: Java中的基本数据类型变量和对象的名称引用变量如定义在方法中,都为局部变量.但对象本身不一定是局部生命周期.如函数外存在其他对该对象的引用变量,则该对象的生命周期延伸至该其他引用变量所在的块. 如从被调用函数参数引用传值或返回值到主调用函数所在的对象类型变量中,则该对象都仍存在(但被调用函数的该对象的引用变量生命周期结束,因此引用变量是局部变量),此时对象突破了局部变量的局部生命期. Java对象销毁 Java

随机推荐