常用接口 - 比较Comparable、比较器Comparator
比较接口
在创建自己的数据类型时,实现了 Comparable 接口就能够保证用例代码可以将其排序 -- 算法第四版
记录的一个原因是学习的过程中用得上,另一个原因是查到的高分博客,甚至没有解释返回值的意义。。。
JDK文档
Compares this object with the specified object for order. Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.
稍微翻译
this.value - 当前对象的值
that.value - 外部的值
this.value 和 that.value 做比较:
返回的值可以是 {负整数, 0, 正整数 }
对应的情况是 this.value { 小于, 等于, 大于} that.value
Comparable.java
源码
public interface Comparable<T> {
public int compareTo(T o);
}
实现结构
如上述的分析,只要让this每个需要排序值都去做比对:
this.value[i] > that.value[i] -------> return +1;
this.value[i] = that.value[i] -------> return 0;
this.value[i] < that.value[i] -------> return -1;
即可
例如这里有一个Student
类,含有 age, grade, score 三个属性
public class Student implements Comparable<Student> {
private filnal int age;
private filnal int grade;
private filnal int score;
...
public int compareTo (Student that) {
if (this.age > that.age) {return 1;}
if (this.age < that.age) {return -1;}
if (this.grade > that.grade) {return 1;}
if (this.grade < that.grade) {return -1;}
if (this.score > that.score) {return 1;}
if (this.score < that.score) {return -1;}
return 0;
}
}
如上述代码实现 Comparable 这一接口分为:
类主体实现Comparable<类名>
不考虑任何类型转化的比较,比较的前提都是相同的类,相同的对象
所以实现接口时,泛型为当前的类
public class Student implements Comparable<Student> {}
compareTo方法
按照规则,逐个填写即可
this.value[i] > that.value[i] -------> return +1;
this.value[i] = that.value[i] -------> return 0;
this.value[i] < that.value[i] -------> return -1;
public int compareTo (Student that) {
if (this.age > that.age) {return 1;}
if (this.age < that.age) {return -1;}
if (this.grade > that.grade) {return 1;}
if (this.grade < that.grade) {return -1;}
if (this.score > that.score) {return 1;}
if (this.score < that.score) {return -1;}
return 0;
}
比较器 Comparator
目前学到的是应用在 Arrays.sort()
中,实现对复杂类的排序
这里也以 Arrays.sort()
方法为例,建立复杂数据结构并定义比较器 Comparator
待比较的Point类
按照注释的说法,排序后应该是
x越大元素越靠后,
同样x的情况下,y越小越靠后
/**
* 假定有一个复合类型数据
* x: 越大越好,或者说越大越靠前,比如 moneyOfPerson
* y: 越小越好,或者说越小越靠前,比如 ageOfPerson
* x 的优先级 >> y
*/
class Point {
private int x;
private int y;
public Point() {
}
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
@Override
public String toString() {
return "Point{" +
"x=" + (x/10000.) +
"万, y=" + y +
"岁}";
}
}
初始化(构造数组)
手动构造一个需要比对y(第二元素)的情况
SortTest.java
Point[] pArray;
SortTest() {
// 这里由于 x 变化范围太大,难以获得一个相等的情况,不利于比对 y
// 因此手动创建一个和前一个值x相等,y不相等的 Point
int N = 10;
pArray = new Point[N];
Random random = new Random();
for (int i = 0; i < N - 1; i++) {
pArray[i] = new Point(random.nextInt(1000000), random.nextInt(100));
}
pArray[N-1] = new Point(pArray[N-2].getX(), pArray[N-2].getY() - 1);
}
比较器Comparator
这里同时重写了
逆序方法 reversed()
比较方法 compare()
只要写好了 compare()
逆序甚至不需要写
class comparatorPoint implements Comparator {
/**
* 逆序
* @return
*/
@Override
public Comparator reversed() {
return Comparator.super.reversed();
}
/**
* 升序排列
* @param o1
* @param o2
* @return o1 和 o2 的比较结论
*
* o1.value[i] > o2.value[i] -------> return +1;
* o1.value[i] = o2.value[i] -------> return 0;
* o1.value[i] < o2.value[i] -------> return -1;
*/
@Override
public int compare(Object o1, Object o2) {
Point t1 = (Point) o1;
Point t2 = (Point) o2;
if (t1.getX() == t2.getX()) {
// x 相等时,y越小越好
// 这里测试一下,越小越好这个含义,是不是和上述注释
// 反着写就可以
if (t1.getY() == t2.getY()) {return 0;}
else if(t1.getY() < t2.getY()) {return +1;}
else {return -1;}
} else if (t1.getX() > t2.getX()) {return +1;}
else {return -1;}
}
}
案例
public static void main(String[] args) {
SortTest sortTest = new SortTest();
System.out.println("初始状态:");
for (Point point : sortTest.pArray) {
System.out.println(point);
}
System.out.println("================================");
System.out.println("排序后:");
Arrays.sort(sortTest.pArray, new comparatorPoint());
System.out.println("正序:");
for (Point point : sortTest.pArray) {
System.out.println(point);
}
System.out.println("逆序:");
Arrays.sort(sortTest.pArray, new comparatorPoint().reversed());
for (Point point : sortTest.pArray) {
System.out.println(point);
}
输出
初始状态:
Point{x=11.353万, y=85岁}
Point{x=2.4705万, y=81岁}
Point{x=47.315万, y=9岁}
Point{x=73.8251万, y=40岁}
Point{x=19.9306万, y=46岁}
Point{x=46.6619万, y=56岁}
Point{x=82.8019万, y=98岁}
Point{x=81.8677万, y=99岁}
Point{x=35.127万, y=19岁}
Point{x=35.127万, y=18岁}
================================
排序后:
正序:
Point{x=2.4705万, y=81岁}
Point{x=11.353万, y=85岁}
Point{x=19.9306万, y=46岁}
Point{x=35.127万, y=19岁}
Point{x=35.127万, y=18岁} // 这里x相同,y越小地位越高,也就越靠后
Point{x=46.6619万, y=56岁}
Point{x=47.315万, y=9岁}
Point{x=73.8251万, y=40岁}
Point{x=81.8677万, y=99岁}
Point{x=82.8019万, y=98岁}
逆序:
Point{x=82.8019万, y=98岁}
Point{x=81.8677万, y=99岁}
Point{x=73.8251万, y=40岁}
Point{x=47.315万, y=9岁}
Point{x=46.6619万, y=56岁}
Point{x=35.127万, y=18岁}
Point{x=35.127万, y=19岁}
Point{x=19.9306万, y=46岁}
Point{x=11.353万, y=85岁}
Point{x=2.4705万, y=81岁}
Process finished with exit code 0
可见效果和设定相符
进一步简化Comparator
可以在使用时创建比较器
内部代码是一样的,注意格式
好处是这种写法 IDEA 会自动推断 Comparator
的类型
不需要做类型转化的操作
Arrays.sort(sortTest.pArray, new Comparator<Point>() {
@Override
public int compare(Point o1, Point o2) {
if (o1.getX() == o2.getX()) {
// x 相等时,y越小越好
// 这里测试一下,越小越好这个含义,是不是和上述注释
// 反着写就可以
if (o1.getY() == o2.getY()) {return 0;}
else if(o1.getY() < o2.getY()) {return +1;}
else {return -1;}
} else if (o1.getX() > o2.getX()) {return +1;}
else {return -1;}
}
});
简化验证
排序后:
正序:
Point{x=3.6459万, y=3岁}
Point{x=12.861万, y=16岁}
Point{x=24.4924万, y=65岁}
Point{x=33.8556万, y=31岁}
Point{x=37.4221万, y=86岁}
Point{x=41.7508万, y=85岁}
Point{x=41.7508万, y=84岁}
Point{x=56.8514万, y=83岁}
Point{x=72.7173万, y=77岁}
Point{x=96.9255万, y=45岁}
匿名正序验证:
Point{x=3.6459万, y=3岁}
Point{x=12.861万, y=16岁}
Point{x=24.4924万, y=65岁}
Point{x=33.8556万, y=31岁}
Point{x=37.4221万, y=86岁}
Point{x=41.7508万, y=85岁}
Point{x=41.7508万, y=84岁}
Point{x=56.8514万, y=83岁}
Point{x=72.7173万, y=77岁}
Point{x=96.9255万, y=45岁}
效果当然是一样的