目录
3. 比较器对象也可作为参数传入指定方法(sort 排序方法)
一、== 和 equals
1. 基本类型
< <= == != > >=
2. 引用类型(对象的比较)
(1)引用1 == 引用2 为 true:引用1 和 引用2 都指向同一个对象。
如:引用1 == 引用2 == null 为 true:两个引用都不指向任何对象。
(2)引用1 .equals(引用2) == true:引用1 指向的对象 和 引用2 指向的对象 是“相等的”。
前提:对象的类正确的重写了 equals 方法。
== : 关于同一性的比较
equals:关于相等性的比较
二、Comparable<> 接口
当要比较的对象是自定义的类的对象时,单纯的“大/小”不能进行判断。这时候就需要用到 Comparable<> 接口。
要使用 Comparable<> 接口来进行比较,需要实现其 compareTo() 方法。
compareTo()方法:
int cmp = p1.compareTo(p2);
cmp < 0:p1 小于 p2
cmp = 0:p1 等于 p2
cmp > 0:p1 大于 p2
1. 实现了Comparable<> 接口的类的使用
public class Demo1 {
public static void main1(String[] args) {
String s = "hello";
String t = "world";
int cmp = s.compareTo(t);
if (cmp < 0) {
System.out.println(s + "小于" + t);
} else if (cmp == 0) {
System.out.println(s + "等于" + t);
} else {
System.out.println(s + "大于" + t);
}
}
public static void main(String[] args) {
Integer i = 1234;
Integer j = 1238;
//包装类会自动拆箱
//自动调用 i.intValue() 和 j.intValue()
int cmp = i.compareTo(j);
if (cmp < 0) {
System.out.println(i + "小于" + j);
} else if (cmp == 0) {
System.out.println(i + "等于" + j);
} else {
System.out.println(i + "大于" + j);
}
}
}
2. 自己定义一个实现 Comparable<> 接口的类
定义一个 Person 类,有身高(height)、体重(weight)、成绩(score)、等级(level)四个属性。Person 类中需要实现 compareTo() 方法,如何实现需要根据具体的自己的规定。
(1)规定 level 大的“大”
public class Person implements Comparable<Person>{
private final int weight;
private final int height;
private final int score;
private final int level;
public Person(int weight, int height, int score, int level) {
this.weight = weight;
this.height = height;
this.score = score;
this.level = level;
}
@Override
public int compareTo(Person o) {
int r = this.level - o.level;
if (r < 0) {
return -1;
} else if (r == 0) {
return 0;
} else {
return 1;
}
//可直接简写为 return this.level - o.level;
}
}
(2)按照 level > score > weight > height 的优先级比较
public class Person implements Comparable<Person>{
private final int weight;
private final int height;
private final int score;
private final int level;
public Person(int weight, int height, int score, int level) {
this.weight = weight;
this.height = height;
this.score = score;
this.level = level;
}
@Override
public int compareTo(Person o) {
int r = this.level - o.level;
if (r != 0) {
return r;
}
r = this.score - o.score;
if (r != 0) {
return r;
}
r = this.weight - o.weight;
if (r != 0) {
return r;
}
//如果代码执行到这里,说明 level、score、weight 都相等
return this.height - o.height;
}
}
(3)按照相应的权重:身高0.3、体重0.2、成绩0.5、等级0.4。综合分比较
public class Person implements Comparable<Person>{
private final int weight;
private final int height;
private final int score;
private final int level;
public Person(int weight, int height, int score, int level) {
this.weight = weight;
this.height = height;
this.score = score;
this.level = level;
}
@Override
public int compareTo(Person o) {
double t = this.height * 0.3 + this.weight * 0.2 + this.score * 0.5 + this.level * 0.4;
double s = this.height * 0.3 + this.weight * 0.2 + this.score * 0.5 + this.level * 0.4;
if ( t < s) {
return -1;
} else if (t == s) {
return 0;
} else {
return 1;
}
}
}
注意:当我们实现compareTo() 方法时,不仅要遵循需要遵循和 equals() 方法的统一。即
a.compareTo(b) == 0 -> a.equals(b) == true
a.compareTo(b) != 0 -> a.equals(b) == false
三、Comparator<> 接口 (比较器)
在java中,不是所有类都实现了Comparable<>接口,如 List接口 以及 ArryList 类。因此有了 Comparator<>接口。该接口是一个比较器,因此如果要用该接口进行比较的话。需要有三个对象:
- 天平对象(需要提前定义天平类)
- 要比较的 o1 对象
- 要比较的 o2 对象
即当我们进行比较时,需要拿出来一个天平(比较器),就是天平对象。然后在天平两端放上我们要进行比较的 o1 对象和 o2 对象。
如要使用Comparator<> 比较器进行比较,需要在天平类中实现 compare() 方法。
compare() 方法:
int cmp = 天平对象.compare(o1, o2);
cmp < 0:o1 小于 o2
cmp = 0:o1 等于 o2
cmp > 0:o1 大于 o2
下面举几个使用例子
1. List 中使用比较器
现有 List1 和 List2 两个存储 int 类型的链表,比较两个链表的大小(根据链表中的元素个数进行比较)。
思路:要比较 List1 和 List2 的大小,需要先判断:List 类是否是 Comparable 接口的实现类 或者 List 接口是否是 Comparable 的子接口,如果都不是,则只能使用 Comparator 比较器。
List 和 ArrayList 都和 Comparable 没有关系,所以使用比较器。
public class Demo1 {
public static void main(String[] args) {
List<Integer> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
list1.add(1);
list1.add(2);
list1.add(3);
list1.add(4);
list2.add(3);
list2.add(4);
list2.add(5);
Comparator<List<Integer>> 天平 = new 基于List的比较器();
int cmp = 天平.compare(list1, list2);
if (cmp < 0) {
System.out.println("list1 小于 list2");
} else if (cmp == 0) {
System.out.println("list1 等于 list2");
} else {
System.out.println("list1 大于 list2");
}
}
//比较器类
static class 基于List的比较器 implements Comparator<List<Integer>> {
@Override
public int compare(List<Integer> o1, List<Integer> o2) {
return o1.size() - o2.size();
}
}
}
2. 本身数据类型具备比较能力,但不符合实际需求
例如天干的比较,甲、乙、丙、丁、戊、己、庚、辛、壬、癸进行比较。
本身的数据类型为 char(Character),具备自然比较能力,但不符合实际比较需求,即甲 < 乙 < 丙 < 丁 < 戊 < 己 < 庚 < 辛 < 壬 < 癸。因此使用比较器进行比较。(这里只比较甲乙)
public class Demo2 {
public static void main(String[] args) {
char c1 = '甲';
char c2 = '乙';
Comparator<Character> 天平 = new 基于天干的比较器();
int cmp = 天平.compare(c1, c2);
if (cmp < 0) {
System.out.println(c1 + "小于" + c2);
} else if (cmp == 0) {
System.out.println(c1 + "等于" + c2);
} else {
System.out.println(c1 + "大于" + c2);
}
}
static class 基于天干的比较器 implements Comparator<Character> {
@Override
public int compare(Character o1, Character o2) {
if (o1.equals('甲')){
if (o2.equals('甲')) {
return 0;
} else {
// o1:甲 o2:乙 甲 < 乙
return -1;
}
} else {
//此时 o1:乙
if (o2.equals('乙')) {
return 0;
} else {
// o1:乙 o2:甲 乙 > 甲
return 1;
}
}
}
}
}
3. 比较器对象也可作为参数传入指定方法(sort 排序方法)
在使用 sort() 方法时(Arrays.sort() 或者 List.sort() 或者 Collections.sort() 方法),传入相应参数对象进行默认排序,该排序时根据自然顺序进行升序排序的。当自然顺序不能满足实际要求时,也可将比较器对象传入 sort 方法中,sort 就会按照比较器中的 compare() 方法进行比较排序。
例一:List 链表中存储的是汉字 "零", "一", "二", "三", "四", "五", "六", "七", "八", "九",但是顺序不确定,现在需要将乱序的汉字排序成该顺序。
public class Demo3 {
public static void main(String[] args) {
List<String> list = Arrays.asList(
"零", "一", "二", "三", "四", "五", "六", "七", "八", "九"
);
//将 s1 中的元素打乱
Collections.shuffle(list);
System.out.println(list);
//直接使用 sort
Collections.sort(list);
System.out.println(list); //[一, 七, 三, 九, 二, 五, 八, 六, 四, 零] 不符合预期
//传入比较器
Comparator<String> c = new CustomComparator();
list.sort(c);
System.out.println(list); //[零, 一, 二, 三, 四, 五, 六, 七, 八, 九]
}
static class CustomComparator implements Comparator<String> {
//将String 转换为 int,方便调用到 compare 方法中
private int transfer(String s) {
switch (s) {
case "零": return 0;
case "一": return 1;
case "二": return 2;
case "三": return 3;
case "四": return 4;
case "五": return 5;
case "六": return 6;
case "七": return 7;
case "八": return 8;
case "九": return 9;
default: return -1;
}
}
@Override
public int compare(String o1, String o2) {
return transfer(o1) - transfer(o2);
}
}
}
例二:使用数组将学生对象存储了起来(Student 类是自己写的,其中属性有学号及身高),现在需要将数组中的学生按照学号/身高进行排序。
Student类:
public class Student {
public final int no;
public final int height;
public Student(int no, int height) {
this.no = no;
this.height = height;
}
@Override
public String toString() {
return "Student{" +
"no=" + no +
", height=" + height +
'}';
}
}
具体实现:
public class Demo4 {
public static void main(String[] args) {
Student[] array = {
new Student(5,170),
new Student(9,175),
new Student(1,160),
new Student(3,180),
};
//当调用Arrays.sort(array)时,因为数组中元素类型没有自然顺序,所以会报错
//Arrays.sort(array):要求元素类型具备自然顺序 && 自然顺序符合预期
// Arrays.sort(array);
//传入比较器
Comparator<Student> c = new StudentsComparator();
Arrays.sort(array,c);
System.out.println(Arrays.toString(array));
}
static class StudentsComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.no - o2.no;
//若按照身高排序,改为 o1.height - o2.height 即可。
}
}
}
四、总结
1. ==:关于同一性比较
2. equals:关于相等性比较
3. Comparable 接口:具备比较能力
(1)方法:int compareTo(T o)
- this < o:负数
- this == o:0
- this > o:正数
(2)使用角度:
- 使用已经实现了 Comparable 接口的类 的对象进行比较 ;
- 定义类的角度:正确的重写了 compraeTo 方法。
4. Comparator 接口:用于比较两个东西的比较器
(1)方法:int compare(T o1, T o2)
- o1 < o2:负数
- o1 == o2:0
- o1 > o2:正数
(2)使用角度:
要比较的两个对象的类,不具备自然顺序,没有实现过 Comparable 接口;
要比较的两个对象的类,具备自然顺序,但是它的自然顺序不符合我们预期。
(3)sort方法:
List 排序:
- Collecations.sort(List list);
- Collections.sort(List list, Comparator c);
- list.sort(Comparator c);
数组排序:
- Arrays.sort(数组);
- Arrays.sort(数组, Comparator c);
对象可以进行比较时很多后续操作的前提,比如:
- 排序(要求 线性表中的元素是可以比较大小的)
- 优先级队列(要求 放入优先级队列的元素时可以比较大小的,因为优先级队列的原理是堆)