如果我实施自定义比较器,除了equals
之外,覆盖compare
是不错的做法?
此外,是否有Comparator
的定义合约?
答案 0 :(得分:4)
当且仅当c.compare(e1,e2)== 0具有与e1.equals(e2)相同的布尔值时,比较器c对一组元素S施加的排序被认为与equals一致。对于S中的每个e1和e2。
当使用能够强加与equals不一致的排序的比较器来排序有序集(或有序映射)时,应该小心。假设带有显式比较器c的有序集(或有序映射)与从集合S中绘制的元素(或键)一起使用。如果由S对S施加的排序与equals不一致,则排序集(或有序映射)将表现得很奇怪。"特别是有序集(或有序映射)将违反集合(或映射)的一般契约,它以等于的方式定义。
所以答案是肯定的,这是一个好习惯(至少是你的第一个问题)。
答案 1 :(得分:4)
比较者的合同定义为in its javadoc。特别是:
当使用能够强加与equals不一致的排序的比较器来排序有序集(或有序映射)时,应该小心。假设带有显式比较器c的有序集(或有序映射)与从集合S中绘制的元素(或键)一起使用。如果由S对S施加的排序与equals不一致,则排序集(或有序映射)将表现得“奇怪”。特别是有序集(或有序映射)将违反集合(或映射)的一般契约,它以等于的方式定义。
通常,如果2个对象从equals
透视图相等但不是compareTo
透视图,则可以将这两个对象存储为TreeMap中的键。这可能导致不直观的行为。它也可以在特定情况下有目的地完成。
例如,this answer显示了一个示例,其中需要等于和compareTo不一致。
答案 2 :(得分:3)
我认为如果你觉得你在测试平等,你只需要覆盖equals。通常当我编写比较器时,他们正在比较对象中的属性,并继续在这些属性上使用compareTo。
例如
public class ToCompare {
public static final Comparator<ToCompare> ID_COMPARATOR = new Comparator(){
@Override
public int compare(ToCompare o1, ToCompare o2) {
return o1.id.compareTo(o2.id);
}
}
private Integer id;
//... other fields here
}
我倾向于将这些比较器存储在对象中作为公共静态最终字段,以便可以从任何可能要对此对象进行排序的代码访问它们。如果对象被更新(添加/删除了字段),那么我可以立即看到比较器是否存在问题,而不是通过我所有的代码查找问题。
答案 3 :(得分:1)
是。正如Comparator的java doc中所建议的那样 我想说这将取决于你的需求。
例如,假设有一个元素a和b添加两个元素a(a.equals(b)&amp;&amp; c.compare(a,b)!= 0)到具有比较器c的空TreeSet。第二个添加操作将返回true(并且树集的大小将增加)因为a和b在树集的透视图中不等效,即使这与Set.add方法的规范相反。
但是如果你考虑下面的例子,你只需要对特定列表进行排序,那么你可能不必这样做。
让我们举一个类Employee
的例子,它有两个属性id
,name
。
class Employee {
int id;// sample so access not considered
String name;// sample so access not considered
}
假设有两个单独的选项可以分别对id
或name
上的列表进行排序。为了实现这一目标,我需要实现两个比较器,一个比较id
和compares
名称。
现在,如果我想实现基于id
的比较器,因为它的类型为int
,因此包装类型为Integer
,而Integer
提供compareTo
方法用它。
String
的情况也是如此。这样您就不必担心编写compare
或compareTo
方法。
在编写compare
方法时我几乎不需要考虑,因为我总是在对象的属性上使用内置方法。
class EmpIdComparator implements Comparator<Employee> {
@Override
public int compare(Employee o1, Employee o2) {
return Integer.valueOf(o1.id).compareTo(o2.id);// Make use of
// existing
// comparators
// provided by java
}
}
class EmpNameComparator implements Comparator<Employee> {
@Override
public int compare(Employee o1, Employee o2) {
return o1.name == null ? o2.name == null ? 0 : 1 : o1.name
.compareTo(o2.name);// Make use of
// existing
// comparators
// provided by java
}
}
答案 4 :(得分:0)
我觉得其他答案都错过了重点:Comparator
的compare-method比较了两个给定的参数,而Comparator
的equals-method比较了Comparator
本身与另一个对象。
至少较新的JDK的Comparator#equals
的javadoc详细解释了它:
/**
* Indicates whether some other object is "equal to" this
* comparator. This method must obey the general contract of
* {@link Object#equals(Object)}. Additionally, this method can return
* <tt>true</tt> <i>only</i> if the specified object is also a comparator
* and it imposes the same ordering as this comparator. Thus,
* <code>comp1.equals(comp2)</code> implies that <tt>sgn(comp1.compare(o1,
* o2))==sgn(comp2.compare(o1, o2))</tt> for every object reference
* <tt>o1</tt> and <tt>o2</tt>.<p>
*
* Note that it is <i>always</i> safe <i>not</i> to override
* <tt>Object.equals(Object)</tt>. However, overriding this method may,
* in some cases, improve performance by allowing programs to determine
* that two distinct comparators impose the same order.
*
* @param obj the reference object with which to compare.
* @return <code>true</code> only if the specified object is also
* a comparator and it imposes the same ordering as this
* comparator.
* @see Object#equals(Object)
* @see Object#hashCode()
*/
boolean equals(Object obj);
因此,覆盖Comparator
的equals方法很少有用。但Comparable
会有所不同。