Java常用类库之比较器简介
简介
在Java项目开发的机制之中,比较器是一中最为常见的功能,同时在整个的Java类集实现架构之中,比较器都有着非常重要的地位,但是首先应该知道为什么要使用比较器?
我们都知道在Java里面提供有一个Arrays类,这个Arrays类提供有大量的数组有关的操作方法,而其中可以发现这样的一个方法定义:
public static void sort(Object[] a)
发现Arrays类也可以直接实现对象数组的排序处理,于是下面就按照此方法的规则进行程序的编写:
范例: 实现对象数组的排序
class Book{
private String title;
private double price;
public Book() {
}
public Book(String title, double price) {
this.title = title;
this.price = price;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "【Book】图书名称:"+this.getTitle()+"、图书价格:"+this.getPrice();
}
}
public class Demo {
public static void main(String[] args) {
Book[] books = new Book[]{
new Book("Java从入门到精通",99.8),
new Book("Python从入门到精通",96.8),
new Book("Go语言从入门到精通",89.7)
};
Arrays.sort(books);//数组排序处理
System.out.println(Arrays.toString(books));//实现对象数组的字符串转换
}
}
执行结果:
Exception in thread "main" java.lang.ClassCastException: com.itheima.jiuyeban.比较器.Book cannot be cast to java.lang.Comparable
at java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:320)
at java.util.ComparableTimSort.sort(ComparableTimSort.java:188)
at java.util.Arrays.sort(Arrays.java:1246)
at com.itheima.jiuyeban.比较器.Demo.main(Demo.java:51)
在程序执行的过程中出现一个“ClassCastException”异常,这种异常所描述的就是对象转换异常,这里面直接提示给用户“不能够将类的对象实例转为Comparable”。为什么现在会出现这样的异常呢?
其实很好理解,如果说现在给定的是一个整型数组,那么如果要想确定数组之中的元素彼此之间的大小关系,直接利用各种关系运算符即可,但是问题是此时所给出的是一个对象数组,对象数组里面所包含的内容一个个堆内存的信息,那么请问堆内存的信息如何进行大小关系的比较呢?
很明显,堆内存无法直接进行大小关系的比较,如果要进行排序处理,严格意义上来讲应该使用堆内存中属性的内容来进行大小关系的确认,而这个属性内容的确认就必须采用比较器来支持,而在Java里面支持有两种比较器:Comparable、Compatator。
Comparable接口
在比较器的应用过程之中,java.lang.Comparable实在整个Java开发过程之中使用最多的一中比较器,同时很多系统内部提供的类也都是基于Comparable实现的比较器应用,首先来观察一下该接口的基本定义:
public interface Comparable<T>{
public int compareTo(T o);
}
Comparable接口上追加泛型主要的因素在于,如果真的要进行一组对象的比较,那么肯定这组对象应该拥有相同的类型。在这个接口里面只提供有一个核心的比较方法:compareTo(),这个方法在最终实现比较的时候会有三种返回结果:大于(1,比0大的数值)、等于(0)、小于(-1,比0小的数值)。
范例: 使用Comparable 接口实现排序功能
class Book implements Comparable<Book>{ // 表示这个类拥有排序支持
private String title;
private double price;
public Book() {
}
public Book(String title, double price) {
this.title = title;
this.price = price;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "【Book】图书名称:"+this.getTitle()+"、图书价格:"+this.getPrice()+ "\n";
}
@Override
public int compareTo(Book o) { //可以自定义排序规则
if(this.getPrice() > o.getPrice()){
return 1;
}else if(this.getPrice() < o.getPrice()){
return -1;
}else{
return 0;
}
}
}
public class Demo {
public static void main(String[] args) {
Book[] books = new Book[]{
new Book("Java从入门到精通",99.8),
new Book("Python从入门到精通",96.8),
new Book("Go语言从入门到精通",89.7)
};
Arrays.sort(books);
System.out.println(Arrays.toString(books));
}
}
执行结果:
[【Book】图书名称:Go语言从入门到精通、图书价格:89.7
, 【Book】图书名称:Python从入门到精通、图书价格:96.8
, 【Book】图书名称:Java从入门到精通、图书价格:99.8
]
以上的Book类实现了Comparable接口,这样就可以直接在Book类中利用compareTo()方法实现比较规则的定义,那么就可以通过Arrays.sort()方法实现对象数组的排序处理。
Comparator接口
Comparable比较器是在类定义的时候为类设计的额外功能,但是如果说现在假设有一个类在设计之初并没有考虑到排序的需求,那么这个时候如何利用系统类所提供的数组操作形式进行排序呢?为了解决这个问题,在java.util包中提供了一个Comparator比较器接口,这个接口可以实现挽救的比较操作,但是需要定义一个专属的比较器实现类。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZJl4eeLF-1590600053239)(https://gitee.com/gitee_lz/BlogImages/raw/master/img//20200528004647.jpg)]
java.util.Comparator接口实际上其内部的组成结构要比java.lang.Comparable更加的丰富,其核心的方法定义如下:
@FunctionalInterface
public interface Comparator<T>{
public int compare(T o1,T o2);
}
通过compare()方法实现两个对象彼此之间大小关系的比较,同时如果要想将Comparator比较器应用在Arrays类上,则也需要更换相应的排序方法:
public static <T> void sort(T[]a,Comparator<? super T> c)
范例: 使用Comparator实现对象数组的排序
class Book { // 表示这个类拥有排序支持
private String title;
private double price;
public Book() {
}
public Book(String title, double price) {
this.title = title;
this.price = price;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "【Book】图书名称:" + this.getTitle() + "、图书价格:" + this.getPrice() + "\n";
}
}
class BookComparator implements Comparator<Book> { //提供专属的比较器实现类
@Override
public int compare(Book o1, Book o2) {
if (o1.getPrice() > o2.getPrice()) {
return 1;
} else if (o1.getPrice() < o2.getPrice()){
return -1;
}else{
return 0;
}
}
}
public class Demo {
public static void main(String[] args) {
Book[] books = new Book[]{
new Book("Java从入门到精通", 99.8),
new Book("Python从入门到精通", 96.8),
new Book("Go语言从入门到精通", 89.7)
};
Arrays.sort(books,new BookComparator());//这时调用Arrays类的排序方法时,需要指定专属比较器
System.out.println(Arrays.toString(books));
}
}
执行结果:
[【Book】图书名称:Go语言从入门到精通、图书价格:89.7
, 【Book】图书名称:Python从入门到精通、图书价格:96.8
, 【Book】图书名称:Java从入门到精通、图书价格:99.8
]
此时单独编写了一个Comparator接口对象子类实现具体的比较规则,同时由于Comparator接口上使用了函数式接口的注解定义(@FunctionalInterface),那么也可以通过Lambda表达式来进行处理。
范例: 通过Lambda实现自定义比较规则
class Book { // 表示这个类拥有排序支持
private String title;
private double price;
public Book() {
}
public Book(String title, double price) {
this.title = title;
this.price = price;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "【Book】图书名称:" + this.getTitle() + "、图书价格:" + this.getPrice() + "\n";
}
}
public class Demo {
public static void main(String[] args) {
Book[] books = new Book[]{
new Book("Java从入门到精通", 99.8),
new Book("Python从入门到精通", 96.8),
new Book("Go语言从入门到精通", 89.7)
};
Comparator<Book> comparator = (o1, o2) -> {
if (o1.getPrice() > o2.getPrice()) {
return 1;
} else if (o1.getPrice() < o2.getPrice()){
return -1;
}else{
return 0;
}
};
Arrays.sort(books,comparator);//这时调用Arrays类的排序方法时,需要指定专属比较器
System.out.println(Arrays.toString(books));
Arrays.sort(books,comparator.reversed());//数组内容反转
System.out.println(Arrays.toString(books));
}
}
执行结果:
[【Book】图书名称:Go语言从入门到精通、图书价格:89.7
, 【Book】图书名称:Python从入门到精通、图书价格:96.8
, 【Book】图书名称:Java从入门到精通、图书价格:99.8
]
[【Book】图书名称:Java从入门到精通、图书价格:99.8
, 【Book】图书名称:Python从入门到精通、图书价格:96.8
, 【Book】图书名称:Go语言从入门到精通、图书价格:89.7
]
Comparator除了基本的排序支持之外,其内部实际上也存在有大量的数据排序的处理操作,例如:reversed(),如果现在使用的是Comparable接口实现这样的反转那么必须进行大量系统源代码的修改,但是如果使用了Comparator可以在外部通过具体的方法来进行配置,所以灵活度更高。
总结:两种比较器的区别?
- java.lang.Comparable: 是在类定义的时候实现的接口,该接口只存在有一个compareTo()方法用去确定大小关系;
- java.util.Comparator: 是属于挽救的比较器,除了可以实现排序的功能之外,在JDK1.8之后的版本里面还提供有更多方便的数组操作的处理功能。