2012-10-04 80 views
5

Si implemento un comparador personalizado, ¿se considera una buena práctica anular equals además de compare?
Además, ¿existe un contrato definido para Comparator?Mejor práctica del comparador

+0

Un error común es mutar un campo usado en hashCode, equals o compareTo. Los haría finales e inmutables cuando sea posible. –

+0

¿Quiere decir 'Comparable'? (O 'compare'?) –

+0

@ TomHawtin-tachado: OP – Jim

Respuesta

4

El contrato de Comparador se define in its javadoc. En particular:

Se debe tener precaución cuando se utiliza un comparador capaz de imponer un ordenamiento incompatible con iguales para ordenar un conjunto ordenado (o mapa ordenados). Supongamos que un conjunto ordenado (o un mapa ordenado) con un comparador explícito c se usa con elementos (o claves) extraídos de un conjunto S. Si el ordenamiento impuesto por c sobre S es inconsistente con los iguales, el conjunto ordenado (o el mapa ordenado) comportarse "extrañamente". En particular, el conjunto ordenado (o el mapa ordenado) violará el contrato general para el conjunto (o mapa), que se define en términos de iguales.

Por lo general, si 2 objetos son iguales desde el punto de vista equals pero no desde un punto de vista compareTo, puede almacenar tanto los objetos como claves en un TreeMap. Esto puede conducir a un comportamiento no intuitivo. También se puede hacer a propósito en situaciones específicas.

Por ejemplo, this answer muestra un ejemplo donde es deseable que sea igual y compare Para ser inconsistente.

4

Los javadoc for Comparator estados:

El orden impuesto por un comparador c en un conjunto de elementos de S se dice que es consistente con los iguales si y sólo si c.compare (e1, e2) == 0 tiene el mismo valor booleano que e1.equals (e2) para cada e1 y e2 en S.

Se debe tener precaución al usar un comparador capaz de imponer un orden incoherente con iguales para ordenar un conjunto ordenado (o un mapa ordenado). Supongamos que un conjunto ordenado (o un mapa ordenado) con un comparador explícito c se usa con elementos (o claves) extraídos de un conjunto S. Si el ordenamiento impuesto por c sobre S es inconsistente con los iguales, el conjunto ordenado (o el mapa ordenado) comportarse "extrañamente". En particular, el conjunto ordenado (o el mapa ordenado) violará el contrato general para el conjunto (o mapa), que se define en términos de iguales.

Entonces la respuesta sería sí, es una buena práctica (al menos para su primera pregunta).

+0

actualizado Lo que usted cita no dice que es una buena práctica. Solo indica cuando los iguales y compare son consistentes. – assylias

+0

@assylias Cierto, agregué el segundo párrafo del javadoc para dejarlo en claro. – maba

3

Creo que solo tiene que anular iguales si siente que está probando la igualdad. A menudo, cuando escribo comparadores, comparan propiedades dentro de un objeto y continúo usando compareTo en estas propiedades.

Por ejemplo

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 

} 

tiendo a almacenar estos comparadores en el objeto como campos static final públicas para que sean accesibles desde cualquier código que lo desea, puede ordenar este objeto. Si el objeto se actualiza (campos agregados/eliminados) entonces puedo ver de inmediato si hay problemas con el comparador en lugar de revisar todos mis problemas de búsqueda de códigos.

1

Sí. como se sugiere en el documento de Java de Comparator , diría que dependerá de sus necesidades.

Por ejemplo, supongamos que se añade dos elementos a y b tales que (a.equals (b) & & c.compare (a, b)! = 0) a un TreeSet vacío con comparador c. La segunda operación de adición devolverá verdadera (y el tamaño del conjunto de árboles aumentará) porque a y b no son equivalentes desde la perspectiva del conjunto de árboles, aunque esto sea contrario a la especificación del método Set.add.

Pero si se considera el ejemplo de abajo, donde solo tiene que ordenar una lista en particular, es posible que no sea necesario.

Tomemos un ejemplo de la clase Employee que tiene dos propiedades id, name.

class Employee { 
    int id;// sample so access not considered 
    String name;// sample so access not considered 
} 

Supongamos que hay dos opciones separadas a la lista de id o name separado tipo. Para lograr esto, necesitaría implementar dos comparadores uno que se compare en id y otro en el nombre compares.

Ahora bien, si desea implementar comparador basado en id como su tipo es tan int tipo de envoltorio es Integer y como Integer proporciona compareTo método sólo lo utilizan.

Igual es el caso de String. De esta forma, no tendrá que preocuparse por escribir el método compare o compareTo.

No necesito pensar mientras escribo el método compare ya que siempre uso métodos integrados en las propiedades de los objetos.

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 
    } 

} 
+0

¿Debo ocuparme de 'null'? ¿No es el contrato tirar' NPE' para argumentos 'null'? – Jim

+0

@Jim Si el comparador no permite nulo, entonces debería arrojar NPE de otra manera no. –

+0

Entonces, ¿no hay un contrato global para esto? – Jim

0

siento que las otras respuestas pierden el punto: Comparator 's-compare método compara los dos parámetros dados, mientras que un Comparator' s es igual método compara el Comparator sí con otro objeto.

el Javadoc para Comparator#equals de al menos más nuevo JDK explica en detalle:

/** 
* Indicates whether some other object is &quot;equal to&quot; 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); 

Como tal, es rara vez útil para anular el iguales-método de Comparator. Comparable sería diferente, sin embargo.