首页 » 编写高质量代码:改善Java程序的151个建议 » 编写高质量代码:改善Java程序的151个建议全文在线阅读

《编写高质量代码:改善Java程序的151个建议》建议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方法,确保两者同步。