搜狐财经峰会,网站seo查询站长之家,o2o商城网站搭建,重庆关键词排名推广哈喽#xff0c;大家好#xff01;我是保护小周ღ#xff0c;本期为大家带来的是Java中自定义类型#xff08;对象#xff09;的三种比较方式#xff0c;equals 方法, Comparable 泛型接口, Comparator 泛型接口 。在日常编程中#xff0c;我们常常会需要比较的问题大家好我是保护小周ღ本期为大家带来的是Java中自定义类型对象的三种比较方式equals 方法, Comparable 泛型接口, Comparator 泛型接口 。在日常编程中我们常常会需要比较的问题那么对于我们的基本数据类型来讲可以直接使用 比较运算符来比较大小 , , , , …… 实际中在Java开发中我们经常使用的是“类”类型也就是对象那么作为一个对象又该怎么样去比较数据呢一、基本数据类型及Integer包装类的比较几乎任何一种编程语言都支持基本数据类型比较。但是对于基本数据来讲背后还有功能更加强大的包装类也就是引用类型。引用类型的数据为什么不能比较呢首先画个图浅浅的理解一下。Integer valA new Integer(10);为什叫引用类型呢因为访问数据是通过Java栈区上的引用变量存储的地址来达到访问Java 堆区上对象的数据可以理解为头在栈区身体在堆区只有通过头才能找到身体引用类型的本质是地址.那么一块地址值又怎么能够比较即使可以比较那么又怎么能达到我们想要的效果呢。理论上引用类型是不可以比较的但是有一个特例就是Integer 包装类。1.1 包装类的装箱/装包操作装箱/装包 把基本数据类型转换成 引用类型对应的包类型在进行自动“装箱”的时候IDEA会自动调用一个 Integer.valueOf() 方法来帮我们转换由这个方法我们可以看到当数值的范围在 [-128, 127] 的范围内存储的是基本数据类型所以在这个范围内的 Integer 类型的数据是可以直接使用比较运算符比较的。当数值处在[-128, 127] 的范围内我们Integer 的底层实际上就是调用数组来存储数据。如果我们超过这个范围就不行了原因是变成“引用类型了。地址是无法比较的。如果我们直接使用关键字 new 来创建一个对象值。其实也不难想象这个时候 valA 和 valB 是妥妥的引用类型即指向对象的地址他们虽然拥有相同的数值但是他们指向了不同的两个对象所以两个地址之间的比较是不可能相同的。我们本质是 期望比较数值 127 的关系很明显达不到我们的要求。二、对象的比较对象的比较有三种比较方式2.1 equals 方法Java 中所有的类都继承于 Object 类Object 类中提供了一个 equals 方法但是 equals 方法实际上比较的是引用变量的存储的地址而不是比较的是引用类型引用的对象的内容。这里是 Object 实现的 equals 方法的实现。public boolean equals(Object obj) {return (this obj);
}
this 表示对当前类引用变量的引用由此可见实际上还是比较两个地址是否相同。所以当我们想要判断两个对象是都相同的时候需要重写我们的 equals 方法按照我们想要的方式来比较对象的内容判断是否相同。定义一个学生类 Student, 成员变量 Id 学号, name 姓名, age 年龄, sex 性别并提供一个构造方法public class Student {public int id; //学号public String name; //姓名public int age; //年龄public String sex; //性别//构造方法public Student(int id, String name, int age, String sex) {this.id id;this.name name;this.age age;this.sex sex;}Overridepublic String toString() { //重写toString 方法方便打印展示效果。return Student{ id id , name name \ , age age , sex sex \ };}
}来到测试类这边 new 两个对象直接使用 equals 方法来比较。很明显是不相同的原因是他是直接比较的两个引用类型存储的地址这两个对象虽然数值相同但是他们是两个独立的对象所以比较的返回值就是不相同的。重写 equals 方法我们设计当对象之间的成员变量的数值相同则认为这两个对象相同。首先我们需要在 Student 类中重写因为是基于 Student 类比较嘛子类重写父类方法子类对象在调用该方法时调用重写后的方法没有重写的话默认直接调用父类的方法。快捷键 Alt insert 会弹出这个界面选择 equals() and hashCode, 一路 next 即可。在每个类中在重写equals方法的时侯一定要重写hashcode方法。这是Java官方的语法规定这里还有一个 hashCode() 方法没有介绍hashCode 是通过哈希函数计算映射出这个对象的地址equals 是基于对象的比较。面试问题 equals 比较一样hashCode 一定一样吗 一定一样hachCode 比较一样equals 一定一样吗 不一定为什么呢 hashCode值是依据地址通过某种公式计算来的, 当遇到不同的地址时不能保证计算的结果一定不相同。如果二者是相同的地址那么通过计算后得到的hashCode 值是必然相同的。当只重写equals方法不重写hashCode时违反规定equals相等的对象必须具有相等的哈希码值总结 重写equals 方法如果两个引用指向的是同一个对象直接返回 true如果传参的引用指向的为 null ,或者传入的对象所属的类与比较的类不一致返回 false按照类的这个属性进行比较例如 Id, name , sex 等相同那么认为是同一个学生。String 类型在比较的时候也是调用了 equals 方法是比较字符串是否相同String 也是引用类型嘛涉及到的 Java语法知识1.当发生向上转型之后 此时通过父类的引用只能访问父类自己的成员不能访问子类特有的成员 2.向上转型后 子类重写的后的方法 父类调用该方法此时是调用子类重写后的方法发生动态绑定 3.动态绑定 通过父类引用 引用子类对象重写后的方法 4.发生向上转型之后 如果父类引用想使用子类特有的 成员 需要进行向下转型强制类型转换 5. 向下转型 不安全只能发生在 向上转型 的类型之间 且右边的范围父类大于 左边子类 6. 可以用 instanceof 关键字 判断 向下转型是否安全注意equals 只能按照相等进行比较不能按照大于小于的方式进行比较。重写 equals 方法后就可以判断 两个对象是否相同了。2.2 Comparable 接口类的比较Comparable接口是Java JDK 提供给用户用来实现对象比较的一个泛型接口然后覆写该接口的一个compareTo 方法比较的规则就是在该方法定义的下面我们来看看这个接口的简介public interface ComparableT {public int compareTo(T o);
}以上代码可以看到 compareTo 方法的返回值是整型int 数据返回值 0; 表示this 指向的对象小于 o 指向的对象。返回值 0; 表示this 指向的对象等于 o 指向的对象。返回值 0; 表示this 指向的对象大于 o 指向的对象。我们在面对对自定义类型的时候如果要想按照大小进行比较时在定义类时实现Comparable接口即可然后在类中重写compareTo方法。如果只是Integer , double 等等基本数据类型之间的比较就可以直接实现该接口然后调用 compareTo() 方法。以Student 类为例public class Student implements ComparableStudent{public int id; //学号public String name; //姓名public int age; //年龄public String sex; //性别//构造方法public Student(int id, String name, int age, String sex) {this.id id;this.name name;this.age age;this.sex sex;}/*** 重写 compareTo 方法博主设计 按照 id 来比较数据从而判断对象的大小* param o* return*/Overridepublic int compareTo(Student o) {return this.id - o.id;}Overridepublic String toString() { //重写toString 方法方便打印展示效果。return Student{ id id , name name \ , age age , sex sex \ };}
}可以看到对象之间就可以根据我们的需求来比较对象之间的大小博主首先设计的是 采用 id 作为 关键字来比较但是如果我们想要采用姓名 或者是 年龄 等等来作为关键字来比较对象的大小我们就需要修改compareTo 方法的比较方式。如果我们采用name 来比较数据/*** 重写 compareTo 方法博主设计 按照 name 来比较数据从而判断对象的大小* param o* return*/Overridepublic int compareTo(Student o) {if(this.name.compareTo(o.name) 0) { //字符串的比较 调用 String 的 compareTo 方法return 0;} else if (this.name.compareTo(o.name) 0) {return -1;} else {return 1;}//为了方便理解所以上面写的复杂按照下面的方式更好/*return o1.name.compareTo(o2.name);*/}关于String 类型的对象的比较我们也可以使用 String 类下的 compareTo 方法我们来看看 JDK 是如何实现这个方法的 public int compareTo(String anotherString) {int len1 value.length;int len2 anotherString.value.length;int lim Math.min(len1, len2);char v1[] value;char v2[] anotherString.value;int k 0;while (k lim) {char c1 v1[k];char c2 v2[k];if (c1 ! c2) {return c1 - c2;}k;}return len1 - len2;}首先计算两个字符串的长度转换成字符数组拿最小的lim字符长度来遍历字符串在lim 范围内比较如果字符不相同就返回 两个字符 ASCll 的差值如果在 lim 的范围内字符都相同则返回两个字符串长度的差值。总结1.我们想要按照某个关键字来比较对象之间大小的关系我们可以让实现 Comparable 接口然后根据实际环境的需要重写compareTo 方法。2. 使用Comparable 接口来比较对象之间的大小的关系的时候我们一般是 写死compareTo 方法无法灵活的更改关键字来达到更改对象比较的方式。比如说我们的通讯录可以按照我们的姓氏首字母来排序也可以根据修改日期可以根据号码排序我们使用 compareTo 方法比较岂不是每次都要修改源代码这是不现实的那怎么办呢接下来会讲。3. Comparable 是 Java.lang 中的接口类可以直接使用。2.3 Comparator 接口基于比较器比较使用这个接口需要我们自己定义一个比较器类然后实现 Comparator泛型接口再重写 compare() 方法。class idComparator implements ComparatorStudent{ //通过学号来比较大小Overridepublic int compare(Student o1, Student o2) {return o1.id - o2.id;}
}兄弟们注意Comparator 跟 Comparable 的区别。class idComparator implements ComparatorStudent { //通过学号来比较大小Overridepublic int compare(Student o1, Student o2) {return o1.id - o2.id;}
}class nameComparator implements ComparatorStudent { //通过姓名来比较大小Overridepublic int compare(Student o1, Student o2) {if(o1.name.compareTo(o2.name) 0) { //字符串的比较 调用 String 的 compareTo 方法return 0;} else if (o1.name.compareTo(o2.name) 0) {return -1;} else {return 1;}}
}public class Test3 {public static void main(String[] args) {Student student1 new Student(202300,李三,19,男);Student student2 new Student(202301,张四,19,男);idComparator idComparator new idComparator(); //按照 id 来比较System.out.println(idComparator.compare(student1, student2));nameComparator nameComparator new nameComparator();// l 在 z 的前面嘛所以返回的是 1;System.out.println(nameComparator.compare(student1, student2)); }
}采用这种方式我们才可能实现关键字随需变换的机制但是还有一点问题就是我们我们虽然写了很多比较类但是我们怎么做到在Student类外传入比较器从而改变Student类内部的比较方式呢如果我们在 Student 类的内部需要比较的形式要求是通过类外影响Student 类的比较机制。首先我们在 Student 类的内部既然需要比较那肯定涉及到方法我们就可以对比较的方法入手如果将比较器以传参的形式输入那么类方法就在内部拿到比较器从而通过传参的比较器从而达到计较的目的。现在摆在我们眼前的是如何接收比较器对象因为我们的需求是可以接收多个比较器对象。以该比较器为例:class idComparator implements ComparatorStudent{ //通过学号来比较大小Overridepublic int compare(Student o1, Student o2) {return o1.id - o2.id;}
}我们使得 idComparator 类继承了ComparatorStudent 的泛型接口然后我们重写了接口的 compare() 方法两个Student对象参数根据我们设计的某种机制比较大小返回 int 类型的数据。所以我们可以采用 Comparator 接口来接收Student泛型对象。接口作为方法的参数,可以接收该接口的所有实现类的对象接口作为方法的返回值,可以返回该接口的所有实现类的对象JDK 关于这方面的实现是使用 Comparator? super T 来接收比较器对象。Comparator? super T 代表任意T的父类或祖先Comparator? super Student可以表示接受任意一个泛型类型是Student父类的Comparator比如一个ComparatorPerson可以给所有Person人比较那么自然也可以给Student比较。 是通配符表示可传入的类型不确定通常需要限制一下范围super T就是限定条件表示 只能接收类型及其父类型。 T 类型此时是接收了 Student 类型。? extends T可以接收T类型或者T类型的子类泛型的上限 super T可以接收E类型或者E的父类型 泛型的下限话不多说直接上代码 /*** 冒泡排序 从小到大排序* param stu* param c* return*/public static T T[] bubbleSort(T[] stu, Comparator? super T c) {if(stu null || c null) {return stu;}for (int i 0; i stu.length - 1; i) {for (int j 1; j stu.length - i; j) {if(c.compare(stu[j - 1],stu[j]) 0) {T tmp stu[j - 1];stu[j - 1] stu[j];stu[j] tmp;}}}return stu;}为了方便理解博主简单的写了一个冒泡排序然后该方法是静态方法可以直接使用类名调用静态的泛型方法需要声明一下类型 T。可以看到非常的成功如果我们想要根据姓名来排序直接new 一个我们设计的name类来作为参数即可。总结1. equals 适用于比较两个对象之间的相等于否。2. Comparable 泛型接口 适用于类的内部比较如果想要对自定义对象按照某种方式来进行比较则需要该类需要实现 Comparable 接口并重写 compareTo() 方法这种情况下比较方式基本上是写死的。3. Comparable 泛型接口 适用于自定义类型的灵活比较需要自己选择比较器对象提供比较器类可以在类的内部也可以在类的外部传参比较器这个需要根据自己的设计和需求来使用该接口需要重写 compare() 方法。至此Java 的对象的比较博主已经分享完了希望对大家有所帮助如有不妥之处欢迎批评指正。本期收录于博主的专栏——JavaSE适用于编程初学者感兴趣的朋友们可以订阅查看其它“JavaSE基础知识”。感谢每一个观看本篇文章的朋友更多精彩敬请期待保护小周ღ *★,°*:.☆(▽)/$:*.°★* docc即撒后看来大家了类。