在平时写Java的程序的时候,如果要进行一些基本类型的变量的比较,可以很方便得调用Math.max()、Math.min()等方法,如果要对数组或者列表进行排序,也可以用Arrays.sort()和Collections.sort()等已经封装好的方法来进行。但是,如果是一个自定义的类的对象呢?比如自定义的两个图形、两个日期等,这时,应该怎么对这些对象进行大小比较乃至排序呢?
基本类型及其包装类的排序在介绍自定义的类的比较与排序之前,还是先简单回顾一下Java中的基本类型的数据与其包装类的元素所组成的数组的排序方式:
public static void primitiveDataTypeSort() {
// 基本数据类型可以直接排序
int[] ints = {3, 1, 5, 2, 4};
java.util.Arrays.sort(ints);
for(int number: ints)
System.out.print(number + " ");
System.out.println();
// 包装器类型(即对象形式)也可以直接进行排序
Character[] characters = { new Character('a'), new Character('c'),
new Character('d'), new Character('b'),};
java.util.Arrays.sort(characters);
for (Character character: characters) {
System.out.print(character + " ");
}
}
输出:
1 2 3 4 5
a b c d
对于List中的类:
public static void collectionsSortTest() {
List doubleList = new ArrayList();
doubleList.add(10.5);
doubleList.add(-0.5);
doubleList.add(1.5);
for (double num: doubleList) System.out.print(num + " ");
System.out.println();
Collections.sort(doubleList);
for (double num: doubleList) System.out.print(num + " ");
}
输出:
10.5 -0.5 1.5
-0.5 1.5 10.5
可以看到,对于基本类型与其包装类的对象而言,应用Java自带的Arrays.sort()与Collections.sort()可以很方便地对其进行排序操作。
使用Comparable接口Comparable接口定义了compareTo方法,用于对象之间的比较
为了实现这样自定义对象的排序,我们可以将这样的类定义为“可比较”的,为了实现这样的要求,应该使每个对象可以调用一个共同的比较方法。在Java中已经对于这样的“可比较”做了定义,即规定了Comparable接口来对“可比较”进行了抽象,因此对于我们希望其实例之间可以相互比较的自定义的类,需要实现Comparable接口,使每个对象都有共同方法comparable。 Comparable接口的定义如下所示:
package java.lang;
import java.util.*;
public interface Comparable {
public int compareTo(T o);
}
compareTo方法判断这个对象相对于给定对象o的顺序,当这个对象小于、等于或大于给定对象o时,分别返回负整数、0或正整数。
Comparable接口是一个反省接口。在实现该接口时,泛型类型E被替换成一种具体的类型。Java类库中的许多类实现了Comparable接口以定义对象的自然顺序。Byte、Short、Integer、Long、Float、Doule等基本类型的包装类,Character、BigInteger、BigDecimal、Calendar、String以及Date这样常用的类,都实现了Comparable接口。
如在Java API中,Integer类中有如下定义:
public final class Integer extends Number implements Comparable {
//... class body omitted..
@Override
public int compareTo(Integer anotherInteger) {
return compare(this.value, anotherInteger.value);
}
}
因此,整型数字时可以比较的,类似的,其它类型的数字也是可以比较的,字符串是可以比较的,日期也是如此。若需要直接比较其大小,可以使用compareTo方法来进比较。
对于compareTo方法直接进行调用的效果如下:
public static void compareToTest() {
System.out.println(new Integer(3).compareTo(new Integer(5)));
System.out.println("ABC".compareTo("ABE"));
java.util.Date date1 = new java.util.Date(2013, 1, 1);
java.util.Date date2 = new java.util.Date(2012, 1, 1);
System.out.println(date1.compareTo(date2));
}
输出:
-1
-2
1
第一行显示一个负数,因为3小于5,第二行i按时一个负数,因为ABC小于ABE,最后显示一个正数,因为date1大于date2。
由于所有Comparable对象都有compareTo()方法,如果对象是Comparable接口类型的实例的话,Java API中的java.util.Arrays.sort(Object[])方法就可以使用compareTo()方法来对数组中的对象进行比较和排序。
下面,我们来自定义一个类来实验这个接口的用法。首先,我们定义一个圆圈的类Circle:
public class Circle {
private double radius;
public Circle() { this.radius = 0; }
public Circle(double radius) { this.radius = radius;}
public double getArea() { return Math.PI * radius * radius; }
public void getName() {System.out.print(" Circle:" + this.radius);}
}
这时,如果我们定义一个Circle类型的数组,然后直接对其进行排序,就会得到一个“未实现Comparable接口”的错误,如下:
public static void main(String[] args) {
Circle[] circles = new Circle[] {new Circle(4), new Circle(3), new Circle(5)};
for (Circle circle: circles)
System.out.println(circle.getArea());
Arrays.sort(circles);
}
输出:
50.26548245743669
28.274333882308138
78.53981633974483
Exception in thread "main" java.lang.ClassCastException: learning_java.sortTry.Circle cannot be cast to java.lang.Comparable
由输出的前三行显示可以看到我们已经成功地得到了三个圆圈的实例,但是由于Circle类并没有实现Comparable接口,因此不能直接使用Arrays.sort()方法来对这个数组进行排序。
这时,我们重新定义一个实现了Comparable接口的圆圈的类CircleComparable:
public class CircleComparable
extends Circle
implements Comparable {
public CircleComparable(double radius) {
super(radius);
}
public int compareTo(CircleComparable circleToCom) {
if (this.getArea() > circleToCom.getArea())
return 1;
else if (this.getArea() < circleToCom.getArea())
return -1;
else
return 0;
}
}
在上面的代码中,定义了一个扩展自Circle的类CircleComparable,并在这个扩展类中实现了Comparable接口,用compareTo(CircleComparable circleToCom)方法来比较两个圆的面积。因此,CircleComparable类的实例既是自己的一个实例,也是Circle和Comparable的实例,当然也是Object的一个实例。接下来再来对其进行一次测试:
public static void main(String[] args) {
CircleComparable c = new CircleComparable(1);
System.out.println("c instanceof CircleComparable:\t" + (c instanceof CircleComparable));
System.out.println("c instanceof Circle:\t" + (c instanceof Circle));
System.out.println("c instanceof Comparable:\t" + (c instanceof Comparable));
System.out.println("c instanceof Object:\t" + (c instanceof Object));
Circle[] circles = new CircleComparable[] {new CircleComparable(4), new CircleComparable(3), new CircleComparable(5)};
for (Circle circle: circles)
System.out.print(circle.getArea() + " ");
System.out.println();
Arrays.sort(circles);
for (Circle circle: circles)
System.out.print(circle.getArea() + " ");
}
输出:
c instanceof CircleComparable: true
c instanceof Circle: true
c instanceof Comparable: true
c instanceof Object: true
50.26548245743669 28.274333882308138 78.53981633974483
28.274333882308138 50.26548245743669 78.53981633974483
可以看到,排序后我们得到了正确的升序输出。
接口提供了通用程序设计的一种形式,在这个例子中,如果不同接口,很难使用通用的sort()方法来排序对象,因为必须使用多重继承才能同时继承Comparable和其基类。
在这一部分的最后再提一点,Object类包含equals方法,它的目的就是为了让Object类的子类来覆盖它,以比较对象的内容是否相同。假设Object类包含一个类似于Comparable接口中所定义的comnpareTo方法,那么sort方法可以用来比较一组任意的对象。Object类中是否应该包含一个compareTo方法尚有争论,由于在Object类中没有定义compareTo方法,所以Java中定义了Comparable接口,以便能够对两个Comparable接口的实例对象进行比较。在这里,虽然即使不遵守,编译器也不会进行报错,但是强烈建议compareTo应该与equals保持一致,即对于两个对象o1与o2,o1.equals(o2)为true与o1.compareTo(o2)==0成立的条件应该相同。
使用Comparator接口Comparator可以用于比较没有实现Comparable的类的对象
在前一节中我们已经实现了使用Comparable接口来比较元素,Java API中的许多常用的类,比如Number的子类Integer、BigInteger、BigDecima等与String、Date等类,都实现了Comparable接口,因此,这些类可以直接用于比较。
但是,如果我们所要处理的元素的类没有实现Comparable接口呢?此时这些元素可以进行比较么?在上面,我们举了一个例子,一个没有实现Comparable接口的类Circle,并创建了一个Circle类型的数组,实验表明,并不能直接将其进行排序,那么是不是对于这些类就不能实现比较与排序了呢?
答案是我们可以通过定义一个比较器(comparator)来实现不同类的元素的比较。要做到这一点,需要创建一个实现java.util.Comparator接口的类并重写它的cmopare方法。
在这里,我们先来看一下这个Comparator接口的定义:
public interface Comparator {
// ...
int compare(T o1, T o2);
// ..
}
之后,我们可以通过实现这个接口来写一个Circle类的比较器CircleComparator:
import java.util.Comparator;
public class CircleComparator
implements Comparator {
public int compare(Circle o1, Circle o2) {
if (o1.getArea() > o2.getArea())
return 1;
else if (o1.getArea() < o2.getArea())
return -1;
else
return 0;
}
}
在这个类中,我们通过覆盖compare方法来实现了Comparator接口,完成了一个Circle的比较器。在compare()方法中,对输入的两个Circle的对象的面积进行判断,若第一个圆的面积大于第二个圆的面积,则返回1,反之则返回-1,而若两个圆的面积相同,则返回0。
若简单地调用这个比较器:
Circle circleOne = new Circle(3);
Circle circleTwo = new Circle(5);
CircleComparator comparator = new CircleComparator();
System.out.println(comparator.compare(circleOne, circleTwo)); //的一个圆比第二个圆的面积小,则会返回-1
则输出:
-1
而若我们想要像前面的例子一样,实现数组的排序,我们可以先看一下`Arrays’类的部分源码:
public static void sort(T[] a, Comparator
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?