你好!今天我們要談談比較對象。
嗯……可是這個話題我們不是已經談過不止一次了嗎?:/ 我們知道
我應該什麼時候使用
例如,假設我們有 15% 的時間需要這樣排序。這顯然不足以讓我們將

==
運算符是如何工作的,以及equals()
和hashCode()
方法。比較有點不一樣。以前,我們很可能是指“檢查對像是否相等”。但是將對象相互比較的原因可能完全不同!其中最明顯的是排序。我想如果你被告知對數字或字符串進行排序ArrayList<>
,你將能夠毫無問題地處理這個問題:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Main {
public static void main(String[] args) {
String name1 = "Masha";
String name2 = "Sasha";
String name3 = "Dasha";
List<String> names = new ArrayList<>();
names.add(name1);
names.add(name2);
names.add(name3);
Collections.sort(names);
System.out.println(names);
}
}
控制台輸出:
[Dasha, Masha, Sasha]
如果您還記得Collections
類及其sort()
方法,那就太好了!我想你在數字方面也不會有問題。這裡有一個更具挑戰性的任務:
public class Car {
private int manufactureYear;
private String model;
private int maxSpeed;
public Car(int manufactureYear, String model, int maxSpeed) {
this.manufactureYear = manufactureYear;
this.model = model;
this.maxSpeed = maxSpeed;
}
// ...getters, setters, toString()
}
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Car> cars = new ArrayList<>();
Car ferrari = new Car(1990, "Ferrari 360 Spider", 310);
Car lambo = new Car(2012, "Lamborghini Gallardo", 290);
Car bugatti = new Car(2010, "Bugatti Veyron", 350);
cars.add(ferrari);
cars.add(bugatti);
cars.add(lambo);
}
}
任務其實很簡單。我們有一個Car
類和 3 個 Car 對象。請您對列表中的汽車進行排序好嗎?您可能會問,“它們應該如何排序?” 按名字?按生產年份?按最大速度?很好的問題。目前,我們不知道如何對Car
對象進行排序。而且,很自然地,Java 也不知道這一點!當我們嘗試將對象列表傳遞Car
給該Collections.sort()
方法時,我們得到一個錯誤:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Car> cars = new ArrayList<>();
Car ferrari = new Car(1990, "Ferrari 360 Spider", 310);
Car lambo = new Car(20012, "Lamborghini Gallardo", 290);
Car bugatti = new Car(2010, "Bugatti Veyron", 350);
cars.add(ferrari);
cars.add(bugatti);
cars.add(lambo);
// Compilation error!
Collections.sort(cars);
}
}
事實上,該語言如何知道如何對您編寫的類的對象進行排序?這取決於您的程序需要做什麼。我們必須以某種方式教 Java 比較這些對象。並按照我們想要的方式比較它們。Java 對此有一個特殊的機制:Comparable
接口。為了以某種方式比較和排序我們的Car
對象,類必須實現這個接口,它由一個方法組成compareTo()
::
public class Car implements Comparable<Car> {
private int manufactureYear;
private String model;
private int maxSpeed;
public Car(int manufactureYear, String model, int maxSpeed) {
this.manufactureYear = manufactureYear;
this.model = model;
this.maxSpeed = maxSpeed;
}
@Override
public int compareTo(Car o) {
return 0;
}
// ...getters, setters, toString()
}
請注意我們指定了Comparable<Car>
接口,而不僅僅是Comparable
. 這是一個參數化接口,也就是說,我們必須指定具體的關聯類。原則上可以<Car>
從界面中移除,但Object
默認情況下會基於對象進行比較。代替方法compareTo(Car o)
,我們的類將有:
@Override
public int compareTo(Object o) {
return 0;
}
當然,我們使用它更容易Car
。在方法內部compareTo()
,我們實現了比較汽車的邏輯。假設我們需要按製造年份對它們進行排序。您可能注意到該compareTo()
方法返回一個int
,而不是一個boolean
。不要讓這讓你感到驚訝。當我們比較兩個對象時,有 3 種可能性:
а < b
a > b
a == b
.
boolean
只有 2 個值:true 和 false,這不適用於比較對象。有了int
,一切都簡單多了。如果返回值為> 0
,則a > b
。如果的結果compareTo
是< 0
,那麼a < b
。並且,如果結果為== 0
,則兩個對象相等:a == b
。教我們的班級按製造年份對汽車進行分類很容易:
@Override
public int compareTo(Car o) {
return this.getManufactureYear() - o.getManufactureYear();
}
但是這裡發生了什麼?我們獲取一個 Car 對象 ( this
),獲取這輛車的製造年份,並從中減去另一輛汽車(與該對象進行比較的汽車)的製造年份。如果第一輛汽車的製造年份更大,該方法將返回一個int > 0
. 這意味著this car >
汽車o
。相反,如果第二輛車的製造年份 ( о
) 更大,則該方法將返回一個負數,這意味著o > this
. 最後,如果它們相等,則該方法將返回0
。這個簡單的機制已經足夠我們對Car
對象集合進行排序了!您無需執行任何其他操作。一探究竟:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Car> cars = new ArrayList<>();
Car ferrari = new Car(1990, "Ferrari 360 Spider", 310);
Car lambo = new Car(2012, "Lamborghini Gallardo", 290);
Car bugatti = new Car(2010, "Bugatti Veyron", 350);
cars.add(ferrari);
cars.add(bugatti);
cars.add(lambo);
// There was previously an error here
Collections.sort(cars);
System.out.println(cars);
}
}
控制台輸出:
[Car{manufactureYear=1990, model='Ferrari 360 Spider', maxSpeed=310},
Car{manufactureYear=2010, model='Bugatti Veyron', maxSpeed=350},
Car{manufactureYear=2012, model='Lamborghini Gallardo', maxSpeed=290}]
汽車按照我們想要的方式分類!:) 
Comparable
?中實現的比較方法Comparable
稱為自然排序。這是因為在compareTo()
方法中,您定義了比較此類對象的最常見或最自然的方式。Java 已經有了自然順序。例如,Java 知道字符串通常按字母順序排序,而數字則按數值遞增排序。因此,如果您sort()
在數字或字符串列表上調用該方法,它們將被排序。如果我們的程序通常按製造年份對汽車進行比較和排序,那麼我們應該使用接口Comparable<Car>
和compareTo()
方法。但是,如果這對我們來說還不夠怎麼辦?假設我們的程序不是那麼簡單。在大多數情況下,汽車的自然分類(我們已將其設置為按製造年份執行)適合我們。但有時我們的客戶是快速駕駛的狂熱愛好者。如果我們正在準備一份汽車目錄供他們閱讀,那麼汽車應該按最高速度排序。 
Car
班級的自然排序設置為按速度而不是按製造年份。但是我們不能忽視15%的客戶。那麼我們該怎麼辦?另一個接口在這里為我們提供幫助:Comparator
。就像Comparable
,它是一個參數化的接口。有什麼不同? Comparable
使我們的對象“可比較”並定義它們最自然的排序順序,即在大多數情況下將使用的排序順序。 Comparator
是一個單獨的“比較”界面。如果我們需要實現某種特殊的排序順序,我們不需要深入到類中去Car
改變.class的邏輯compareTo()
。相反,我們可以創建一個單獨的類來實現 Comparator 並教它如何執行我們需要的排序!
import java.util.Comparator;
public class MaxSpeedCarComparator implements Comparator<Car> {
@Override
public int compare(Car o1, Car o2) {
return o1.getMaxSpeed() - o2.getMaxSpeed();
}
}
如您所見,我們的Comparator
非常簡單。我們只需要實現一個接口方法:compare()
. 它以兩個Car
對像作為輸入,並以通常的方式(通過減法)比較它們的最大速度。like compareTo()
,返回an int
,比較的原理是一樣的。我們如何使用它?一切都很簡單:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Car> cars = new ArrayList<>();
Car ferrari = new Car(1990, "Ferrari 360 Spider", 310);
Car lambo = new Car(2012, "Lamborghini Gallardo", 290);
Car bugatti = new Car(2010, "Bugatti Veyron", 350);
cars.add(ferrari);
cars.add(bugatti);
cars.add(lambo);
Comparator speedComparator = new MaxSpeedCarComparator();
Collections.sort(cars, speedComparator);
System.out.println(cars);
}
}
控制台輸出:
[Car{manufactureYear=2012, model='Lamborghini Gallardo', maxSpeed=290},
Car{manufactureYear=1990, model='Ferrari 360 Spider', maxSpeed=310},
Car{manufactureYear=2010, model='Bugatti Veyron', maxSpeed=350}]
Collections.sort()
我們只需創建一個比較器對象並將其與要排序的列表一起 傳遞給方法。當sort()
方法接收比較器時,它不使用Car
類compareTo()
方法中定義的自然排序。相反,它應用由傳遞給它的比較器定義的排序算法。 這樣做有什麼好處?首先,與現有代碼的兼容性。我們創建了一種新的特殊排序方法,同時保留了大部分時間都會使用的現有方法。Car
我們根本就沒碰過課。它是一個Comparable
,所以它仍然是:
public class Car implements Comparable<Car> {
private int manufactureYear;
private String model;
private int maxSpeed;
public Car(int manufactureYear, String model, int maxSpeed) {
this.manufactureYear = manufactureYear;
this.model = model;
this.maxSpeed = maxSpeed;
}
@Override
public int compareTo(Car o) {
return this.getManufactureYear() - o.getManufactureYear();
}
// ...getters, setters, toString()
}
第二,靈活性。我們可以添加任意多的排序算法。例如,我們可以按顏色、速度、重量或汽車在蝙蝠俠電影中的使用次數對汽車進行分類。我們需要做的就是創建一個額外的Comparator
. 就是這樣!今天你學習了兩個非常重要的機制,你在工作中會經常在實際項目中使用它們。但是,如您所知,沒有實踐的理論是無用的。現在是時候鞏固您的知識並完成一些任務了!
GO TO FULL VERSION