时间:2022-03-08 10:17:11 | 栏目:JAVA代码 | 点击:次
验证== 和 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,此时的比较规则也是在比较身份。*/
}
}
打印结果:

给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时也基本上就是代码中所考虑的这几个操作(套路)。
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方法的语法规则
不同的点数比较后所打印出来的结果如下图:

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 | 需要实现一个比较器对象,对待比较类的侵入性弱,但对算法代码实现侵入性 |