讀古今文學網 > 編寫高質量代碼:改善Java程序的151個建議 > 建議75:集合中的元素必須做到compareTo和equals同步 >

建議75:集合中的元素必須做到compareTo和equals同步

實現了Comparable接口的元素就可以排序,compareTo方法是Comparable接口要求必須實現的,它與equals方法有關係嗎?有關係,在compareTo的返回為0時,它表示的是進行比較的兩個元素是相等的。equals是不是也應該對此作出相應的動作呢?我們看如下代碼。


class City implements Comparable<City>{

//城市編碼

private String code;

//城市名稱

private String name;

public City(String_code, String_name){

code=_code;

name=_name;

}

/*code、name的getter/setter方法省略*/

@Override

public int compareTo(City o){

//按照城市名稱排序

return new CompareToBuilder()

.append(name, o.name)

.toComparison();

}

@Override

public boolean equals(Object obj){

if(obj==null){

return false;

}

if(obj==this){

return true;

}

if(obj.getClass()!=getClass()){

return false;

}

City city=(City)obj;

//根據code判斷是否相等

return new EqualsBuilder()

.append(code, city.code)

.isEquals();

}

}


與上一個建議類似,把多個城市對像放在一個List中,然後使用不同的方法查找同一個城市,看看返回值有什麼異常。代碼如下:


public static void main(Stringargs){

List<City>cities=new ArrayList<City>();

cities.add(new City("021","上海"));

cities.add(new City("021","滬"));

//排序

Collections.sort(cities);

//查找對象

City city=new City("021","滬");

//indexOf方法取得索引值

int index1=cities.indexOf(city);

//binarySearch查找到索引值

int index2=Collections.binarySearch(cities, city);

System.out.println("索引值(indexOf):"+index1);

System.out.println("索引值(binarySearch):"+index2);

}


輸出的index1和index2應該一致吧,都是從一個列表中查找相同的元素,只是使用的算法不同嘛。但是很遺憾,結果不一致:


索引值(indexOf):0

索引值(binarySearch):1


indexOf返回的是第一個元素,而binarySearch返回的是第二個元素(索引值是1),這是怎麼回事呢?

這是因為indexOf是通過equals方法判斷的,equals等於true就認為找到符合條件的元素了,而binarySearch查找的依據是compareTo方法的返回值,返回0即認為找到符合條件的元素。

仔細審查一下代碼,我們覆寫了compareTo和equals方法,但是兩者並不一致。使用indexOf方法查找時,遍歷每個元素,然後比較equals方法的返回值,因為equals方法是根據code判斷的,因此當第一次循環時,equals就返回了true, indexOf方法結束,查找到指定值。而使用binarySearch二分法查找時,依據的是每個元素的compareTo方法返回值,而compareTo方法又是依賴name屬性的,name相等就返回0,binarySearch就認為找到元素了。

問題明白了,修改也就很容易了,將equals方法修改成判斷name是否相等即可,雖然可以解決問題,但這是一個很無奈的解決辦法,而且還要依賴我們的系統是否支持此類修改,因為相等邏輯已經發生了很大的變化。

從這個例子中,我們可以理解兩點:

indexOf依賴equals方法查找,binarySearch則依賴compareTo方法查找。

equals是判斷元素是否相等,compareTo是判斷元素在排序中的位置是否相同。

既然一個是決定排序位置,一個是決定相等,那我們就應該保證當排序位置相同時,其equals也相同,否則就會產生邏輯混亂。

注意 實現了compareTo方法,就應該覆寫equals方法,確保兩者同步。