比较器: Comparable 与 Comparator 区别
每博一文案
师父说: 人不能精得过火,太精明的人往往让人生厌,人也别傻的可怜,一腔热血付出却白忙一场。
太精明的人,凡事都想要争个明明白白,每一分钱都要和人计较。把便宜占尽,把好事包揽,
时间长了,即使再好的朋友也会离去,过于精明就成了算计,成了小肚鸡肠,没有人原意和这样的人相处。
太老实的人,更容易被别人欺负,对人付出要看值不值得,你的付出是否会得到他的回应。
帮助别人要看自己能力,做不到的事情就不要勉强自己。别为了面子委屈自己,
拒绝总比敷衍和拖延来的更好,别惯坏了得寸进尺的人,把你的付出当成理所当然,
别纵容了不知感恩的心,把你的好意当成傻子嬉戏。
对你好的人,要加倍珍惜,把你冷的人,要趁早远离,别精地过火,也别傻得可怜,
做人做事,无愧于心就好。
—————— 一禅心灵庙语
文章目录
- 比较器: Comparable 与 Comparator 区别
- 每博一文案
- 1. 自然排序:java.lang.Comparable
- 2. 定制排序:java.util.Comparator
- 3. Comparable 与 Comparator 的区别
- 4. 总结:
- 5. 最后:
在Java中经常会涉及到对象数组的排序问题,那么就涉及到对象之间的比较问题。
-
Java实现对象排序的方式有两种:
-
自然排序: java.lang.Comparable
-
定制排序: java.util.Comparator
1. 自然排序:java.lang.Comparable
- Comparable 的典型实现 (默认都是从小到大排序)
- String : 按照字符串中字符的 Unicode 值进行比较。
- Character : 按照字符的 Unicode 值进行比较。
- 数值类型对应的包装类以及 Biglnteger,BigDecimal ,按照它们对应的数值大小进行比较。默认是升序(从小到大) 排序的。
- Boolean: true 对应的包装类实例大于 false 对应的包装类实例。
- Date,Time 等: 后面的日期时间比前面的日期时间大。
- Comparable j接口强行对实现它的每个类的对象进行整体排序,这种排序被称为 类排序 。
- 实现 Comparable 的类必须实现 其中的
compareTo(object obj)
方法,两个对象即通过 comparTo(Object obj) 方法的返回值来比较大小。
- 默认是升序:比较的规则是:
- 如果当前对象 this > 形参对象 obj ,则返回正整数。
- 如果当前对象 this < 形参对象obj ,则返回负整数。
- 如果当前对象 this == 形参对象 obj,则返回 0
- 实现 Comparable 接口的对象列表(和数组),可以通过
Collections.sort()
或Arrays.sort()
进行排序,实现此接口的对象可以用作,有序映射中的键或有序集合中的元素,无需指定 比较器。 - 比如: 对于 类 C 的每一个 e1 和 e2 来说,当且仅当 e1.comparTo(e2) == 0 与 e1.equals(e2) ,具有相同的 boolean 值时,类 C的自然排序才叫做 equals 一致,建议(虽然不是必需的) 最好使 自然排序 与 equals 一致。
- 说明:
- Java 中的对象,正常情况下,只能进行比较,== 或 != ,不能使用 > 或 < 的,但是在开发场景中,我们需要对多个对象进行排序,言外之意,就需要比较对象的大小。如何实现 ??? 使用 实现 两个接口中的任何一个 Comparable 或 Comparato 。
举例: 使用 Arrays 中的 sort 排序
import java.util.Arrays;
public class Comparable {
public static void main(String[] args) {
String[] arr = {"AA","CC","BB","FF","DD","EE"};
// 排序前:
System.out.println(Arrays.toString(arr));
// 排序后:
Arrays.sort(arr); // 排序默认是(升序)
System.out.println(Arrays.toString(arr));
}
}
举例: 自定义一个商品类,属性:String name(商品名),double price(商品的价格) ,通过商品的价格进行升序排序。
- 像String ,包装类实现了 Comparable 接口,并重写 compareTo() 方法,给出了比较两个对象大小。
- 重写 compareTo() 的规则:
- 如果当前对象this > 形参对象Obj,则返回正整数。
- 如果当前对象 this < 形参对象 Obj,则返回负整数,
- 如果当前对象 this == 形参对象 Obj,则返回 0
- 重写 compareTo() 的规则:
- 在定义类来说,如果需要排序,我们可以让自定义类实现 Comparable 接口,重写 compartTo() 在compareTo(obj) 方法中指明
如何排序的。
自定义类 implements Comparable 实现该接口,并重写了其中的 compareTo()抽象方法 后就可以比较了。
import java.util.Arrays;
class Commodity implements Comparable {
String name;
double price;
public Commodity() {
// 无参构造器
}
public Commodity(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public int compareTo(Object o) {
/**
* 比较规则:
* * 如果当前对象this > 形参对象Obj,则返回正整数。
* * 如果当前对象 this < 形参对象 Obj,则返回负整数,
* * 如果当前对象 this == 形参对象 Obj,则返回。
*/
// 1. 首先判断该类,是否是 Commodity 的实例,不是就不用比较了,类都不一样
if (o instanceof Commodity) {
// 2.强制转换为对应的 Commodity 的实例对象,获取到其中的比较值
Commodity commodity = (Commodity) o;
if(this.price > commodity.price) {
return 1;
} else if(this.price < commodity.price) {
return -1;
} else {
return 0; // 两个对象比较的值相等。
}
}
// 抛出运行异常
throw new RuntimeException("传入的数据类型不一致");
}
@Override
public String toString() {
return "Commodity{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
}
public class ComparableTest {
public static void main(String[] args) {
Commodity[] commodities = new Commodity[5];
commodities[0] = new Commodity("lenovoMouse", 34);
commodities[1] = new Commodity("dellMouse", 43);
commodities[2] = new Commodity("xiaoMouse", 12);
commodities[3] = new Commodity("huaweiMouse", 65);
commodities[4] = new Commodity("roogyao", 35);
Arrays.sort(commodities);
System.out.println(Arrays.toString(commodities));
}
}
修改重写: compareTo() 的比较规则,根据商品的价格进行降序排序
规则:
- 如果当前对象this < 形参对象Obj,则返回正整数。
- 如果当前对象 this > 形参对象 Obj,则返回负整数,
- 如果当前对象 this == 形参对象 Obj,则返回 0
import java.util.Arrays;
class Commodity implements Comparable {
String name;
double price;
public Commodity() {
// 无参构造器
}
public Commodity(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public int compareTo(Object o) {
/**
* 比较规则:
* * 如果当前对象this > 形参对象Obj,则返回正整数。
* * 如果当前对象 this < 形参对象 Obj,则返回负整数,
* * 如果当前对象 this == 形参对象 Obj,则返回。
*/
// 1. 首先判断该类,是否是 Commodity 的实例,不是就不用比较了,类都不一样
if (o instanceof Commodity) {
// 2.强制转换为对应的 Commodity 的实例对象,获取到其中的比较值
Commodity commodity = (Commodity) o;
if(this.price > commodity.price) {
return -1;
} else if(this.price < commodity.price) {
return 1;
} else {
return 0; // 两个对象比较的值相等。
}
}
// 抛出运行异常
throw new RuntimeException("传入的数据类型不一致");
}
@Override
public String toString() {
return "Commodity{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
}
public class ComparableTest {
public static void main(String[] args) {
Commodity[] commodities = new Commodity[5];
commodities[0] = new Commodity("lenovoMouse", 34);
commodities[1] = new Commodity("dellMouse", 43);
commodities[2] = new Commodity("xiaoMouse", 12);
commodities[3] = new Commodity("huaweiMouse", 65);
commodities[4] = new Commodity("roogyao", 35);
Arrays.sort(commodities);
System.out.println(Arrays.toString(commodities));
}
}
升级需求:
先对商品的价格进行升序排序,如果商品的价格是一样的,就对其中的商品名进行二次降序排序。
import java.util.Arrays;
class Commodity implements Comparable {
String name;
double price;
public Commodity() {
// 无参构造器
}
public Commodity(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Commodity{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
@Override
public int compareTo(Object o) {
/**
* 比较规则:
* * 如果当前对象this > 形参对象Obj,则返回正整数。
* * 如果当前对象 this < 形参对象 Obj,则返回负整数,
* * 如果当前对象 this == 形参对象 Obj,则返回。
*/
// 1. 首先判断该类,是否是 Commodity 的实例,不是就不用比较了,类都不一样
if (o instanceof Commodity) {
// 2.强制转换为对应的 Commodity 的实例对象,获取到其中的比较值
Commodity commodity = (Commodity) o;
if (this.price > commodity.price) {
return -1;
} else if (this.price < commodity.price) {
return 1;
} else {
// 对于价格相同的商品,对商品名进行“降序” 二次排序
// 这里我们直接调用 String 类型中已经重写好的 compareTo()方法就进行排序
return this.name.compareTo(commodity.name); // compareTo()默认就是 “升序”
}
}
throw new RuntimeException("传入的数据类型不一致");
}
}
public class ComparableTest {
public static void main(String[] args) {
Commodity[] commodities = new Commodity[5];
commodities[0] = new Commodity("lenovoMouse", 34);
commodities[1] = new Commodity("dellMouse", 34);
commodities[2] = new Commodity("xiaoMouse", 12);
commodities[3] = new Commodity("huaweiMouse", 65);
commodities[4] = new Commodity("roogyao", 65);
Arrays.sort(commodities);
System.out.println(Arrays.toString(commodities));
}
}
解析:
return -this.name.compareTo(commodity.name);
为什么填一个
"-"
符号这里我们直接调用 String 类型中已经重写好的 compareTo()方法就进行排序
这里我们 附加一个 "-1"负数的意思是:compareTo()默认就是 “升序”
升序: this > 参数对象,返回正整数,this < 参数对象,返回负整数
降序: this > 参数对象,返回负整数, this < 参数对象,返回正整数
这两者的结果刚好相反:所以:我们将升序的返回结果 填一个
“-”
负号就变成了:降序同理:将降序的返回结果,填一个
-
负号,就变成了:升序
2. 定制排序:java.util.Comparator
- 当元素的类型没有实现 java.lang.Comparable 接口而又不方便修改代码,或者实现了java.lang.Comparable 接口的排序不合适当前的操作,那么可以考虑使用 Comparator 的对象来排序,强行对多个地下进行整体排序的比较。
- 重写compare(Object o1, Object o2) 方法,比较规则和: Comparable() 是一样的。
- 升序的:比较规则:
- 如果当前对象 this > 形参对象 obj ,则返回正整数。
- 如果当前对象 this < 形参对象obj ,则返回负整数。
- 如果当前对象 this == 形参对象 obj,则返回 0
- 可以将 Comparato 传递给 sort() 方法,如 (Collections.sort) 或 Arrays.sort) ,从而允许在排序顺序上实现精度控制。
- 还可以使用 Comparator 来控制某些数据结构 (如有序 set 或有序映射)的顺序,或者为那些没有自然顺序的对象的 collection 提供排序。
- 一般对于 Comparator的使用都是一次性的,所以一般都是使用匿名的方式
举例: 对 String 数组中的内容进行 降序排序
import java.util.Arrays;
import java.util.Comparator;
public class ComparatorTest {
public static void main(String[] args) {
String[] arr = {"AA", "CC", "DD", "GG", "EE", "FF", "DD", "ZZ"};
// 排序前
System.out.println(Arrays.toString(arr));
// 通过匿名实现 Comparator 接口,并重写其中的 compare()抽象方法
Arrays.sort(arr, new Comparator() {
// 重写 compare()抽象方法,降序
@Override
public int compare(Object o1, Object o2) {
// 1. 首先判断:是否是比较的对象的类型
if (o1 instanceof String && o2 instanceof String) {
// 2. 强制转换为对于的实例对象,获取到比较的值
String s1 = (String) o1;
String s2 = (String) o2;
// 3. 这里我们调用 String 已经重写好的 compareTo()方法,默认是升序
// 这里我们附加上一个 '-' 负号变成,降序
return -s1.compareTo(s2);
}
// 异常可以代替 return 返回值
throw new RuntimeException("比较类型不一致");
}
});
// 排序后的结果:
System.out.println(Arrays.toString(arr));
}
}
3. Comparable 与 Comparator 的区别
- Comparable 和 Comparator 都是接口。都有定义好的比较的抽象方法 , Comparable 的是 comparTo() ,Comparator 的是compare()
- Comparable 的是 comparTo() 是一个参数,比较的是 this 当前对象 与 参数对象的比较
- Comparator 的是compare(Object o1 , Object o2) 的是两个参数,比较的是 参数 o1 对象 与 参数 o2对象的比较
- Comparable 中的 comparTo()的抽象方法已经被大部分 类已经重写了,我们是不可以修改的,比如 String ,Doubel 类就已经重写了 comparTo()抽象方法,并且 String 是被 final 修饰的是无法继承,再重写 comparTo()抽象方法的,就导致 String 中的 comparTo()一直只能时默认的升序了 ,当我们要对 String 类型的数据进行降序 排序时,就无法实现了。这时候,我们就可以通过 重写 Comparator 的是compare()抽象方法,定制自己所需要的排序规则 时降序,还是升序了。
- 一般对于 Comparator的使用都是一次性的,所以一般都是使用匿名的方式
Arrays.sort(arr, new Comparator() {
// 重写 compare()抽象方法,降序
@Override
public int compare(Object o1, Object o2) {
// 1. 首先判断:是否是比较的对象的类型
if (o1 instanceof String && o2 instanceof String) {
// 2. 强制转换为对于的实例对象,获取到比较的值
String s1 = (String) o1;
String s2 = (String) o2;
// 3. 这里我们调用 String 已经重写好的 compareTo()方法,默认是升序
// 这里我们附加上一个 '-' 负号变成,降序
return -s1.compareTo(s2);
}
// 异常可以代替 return 返回值
throw new RuntimeException("比较类型不一致");
}
});
4. 总结:
- 自然排序的当我们想对自定的类,进行排序时可以重写 Comparable 接口中的
int compareTo(T o)
抽象方法,默认一些已经实现 Comparable 接口的并重写了 compareTo()抽象方法,String,Doubel,Arrays 默认时升序的。 - 升序的规则:
- 如果当前对象this > 形参对象Obj,则返回正整数。
- 如果当前对象 this < 形参对象 Obj,则返回负整数,
- 如果当前对象 this == 形参对象 Obj,则返回 0
- 降序则是相反的:
- 如果当前对象this < 形参对象Obj,则返回正整数。
- 如果当前对象 this > 形参对象 Obj,则返回负整数,
- 如果当前对象 this == 形参对象 Obj,则返回 0
- 升序和降序的刚好是相反的,将升序的返回结果 填一个
“-”
负号就变成了:降序同理:将降序的返回结果,填一个-
负号,就变成了:升序。 - 当 Comparable 接口中的抽象方法的排序,无法满足我们的时候,我们可以实现 Comparator 接口中的
int compare(T o1,T o2)
定制自己需要的排序规则 - 一般对于 Comparator的使用都是一次性的,所以一般都是使用匿名的方式
5. 最后:
限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,江湖再见,后会有期 !!!