Estoy implementando un compareTo()
método para una clase simple como esta (para poder usar Collections.sort()
y otras cosas que ofrece la plataforma Java):
public class Metadata implements Comparable<Metadata> {
private String name;
private String value;
// Imagine basic constructor and accessors here
// Irrelevant parts omitted
}
Quiero que el orden natural de estos objetos sea: 1) ordenado por nombre y 2) ordenado por valor si el nombre es el mismo; ambas comparaciones deben ser insensibles a mayúsculas y minúsculas. Para ambos campos, los valores nulos son perfectamente aceptables, por lo compareTo
que no deben romperse en estos casos.
La solución que me viene a la mente es la siguiente: (estoy usando "cláusulas de protección" aquí, mientras que otros prefieren un único punto de retorno, pero eso no viene al caso):
// primarily by name, secondarily by value; null-safe; case-insensitive
public int compareTo(Metadata other) {
if (this.name == null && other.name != null){
return -1;
}
else if (this.name != null && other.name == null){
return 1;
}
else if (this.name != null && other.name != null) {
int result = this.name.compareToIgnoreCase(other.name);
if (result != 0){
return result;
}
}
if (this.value == null) {
return other.value == null ? 0 : -1;
}
if (other.value == null){
return 1;
}
return this.value.compareToIgnoreCase(other.value);
}
Esto hace el trabajo, pero no estoy perfectamente satisfecho con este código. Es cierto que no es muy complejo, pero es bastante detallado y tedioso.
La pregunta es, ¿cómo haría esto menos detallado (al tiempo que conserva la funcionalidad)? No dude en consultar las bibliotecas estándar de Java o Apache Commons si le ayudan. ¿La única opción para simplificar esto (un poco) sería implementar mi propio "NullSafeStringComparator" y aplicarlo para comparar ambos campos?
Ediciones 1-3 : Eddie tiene razón; arreglado el caso anterior de "ambos nombres son nulos"
Sobre la respuesta aceptada
Hice esta pregunta en 2009, en Java 1.6, por supuesto, y en ese momento la solución JDK pura de Eddie era mi respuesta preferida preferida. Nunca tuve la oportunidad de cambiar eso hasta ahora (2017).
También hay soluciones de bibliotecas de terceros, una de Apache Commons Collections 2009 y una de Guava 2013, ambas publicadas por mí, que preferí en algún momento.
Ahora hice que la solución Java 8 limpia de Lukasz Wiktor fuera la respuesta aceptada. Eso definitivamente debería preferirse si está en Java 8, y actualmente Java 8 debería estar disponible para casi todos los proyectos.
fuente
Respuestas:
Usando Java 8 :
fuente
Collections.sort(List)
no funciona cuando la Lista contiene valores nulos, el comentario no es relevante para la pregunta.Simplemente puede usar Apache Commons Lang :
fuente
nullsFirst()
/nullsLast()
.org.apache.commons.lang3
) es "heredado / mal mantenido / de baja calidad" es falsa o, en el mejor de los casos, carece de fundamento. Commons Lang3 es fácil de entender y usar, y se mantiene activamente. Probablemente sea mi biblioteca más utilizada (aparte de Spring Framework y Spring Security): la clase StringUtils con sus métodos de seguridad nula hace que la normalización de entrada sea trivial, por ejemplo.Implementaría un comparador seguro nulo. Puede haber una implementación por ahí, pero es tan fácil de implementar que siempre he implementado la mía.
Nota: Su comparador arriba, si ambos nombres son nulos, ni siquiera comparará los campos de valor. No creo que esto sea lo que quieres.
Implementaría esto con algo como lo siguiente:
EDITAR: Corregidos errores tipográficos en el código de muestra. ¡Eso es lo que obtengo por no probarlo primero!
EDITAR: Promocionado nullSafeStringComparator a estático.
fuente
final
palabra clave no es realmente necesaria (el código Java ya es detallado). Sin embargo, evita la reutilización de parámetros como variables locales (una práctica de codificación terrible). nuestra comprensión colectiva del software mejora con el tiempo, sabemos que las cosas deben ser finales / constantes / inmutables por defecto. Por lo tanto, prefiero un poco más de verbosidad en el usofinal
de declaraciones de parámetros (por muy trivial que sea la función) para obtenerinmutability-by-quasi-default
). La sobrecarga de comprensión / mantenibilidad es insignificante en el gran esquema de las cosas.Consulte la parte inferior de esta respuesta para obtener una solución actualizada (2013) con Guava.
Esto es con lo que finalmente fui. Resultó que ya teníamos un método de utilidad para la comparación de cadenas de seguridad nula, por lo que la solución más simple era hacer uso de eso. (Es una gran base de código; es fácil pasar por alto este tipo de cosas :)
Así es como se define el ayudante (está sobrecargado para que también pueda definir si los nulos son lo primero o lo último, si lo desea):
Entonces, esto es esencialmente lo mismo que la respuesta de Eddie (aunque no llamaría a un método auxiliar estático un comparador ) y el de uzhin también.
De todos modos, en general, hubiera preferido la solución de Patrick , ya que creo que es una buena práctica utilizar bibliotecas establecidas siempre que sea posible. ( Conozca y use las bibliotecas como dice Josh Bloch.) Pero en este caso, eso no hubiera dado el código más simple y limpio.
Editar (2009): versión de las colecciones de Apache Commons
En realidad, aquí hay una manera de simplificar la solución basada en Apache Commons
NullComparator
. Combínelo con las mayúsculas y minúsculasComparator
proporcionadas en laString
clase:Ahora esto es bastante elegante, creo. (Solo queda un pequeño problema: los Comunes
NullComparator
no admiten genéricos, por lo que hay una asignación sin control)Actualización (2013): versión de guayaba
Casi 5 años después, así es como abordaría mi pregunta original. Si codificara en Java, (por supuesto) estaría usando Guava . (Y ciertamente no es Apache Commons).
Ponga esta constante en alguna parte, por ejemplo, en la clase "StringUtils":
Luego, en
public class Metadata implements Comparable<Metadata>
:Por supuesto, esto es casi idéntico a la versión de Apache Commons (ambos usan CASE_INSENSITIVE_ORDER de JDK ), el uso de
nullsLast()
ser la única cosa específica de Guava. Esta versión es preferible simplemente porque Guava es preferible, como una dependencia, a Commons Collections. (Como todos están de acuerdo )Si te estabas preguntando
Ordering
, ten en cuenta que se implementaComparator
. Es bastante útil especialmente para necesidades de clasificación más complejas, lo que le permite, por ejemplo, encadenar varios pedidos usandocompound()
. Leer pedidos explicados para más!fuente
ComparatorChain
uncompareTo
método para que no necesite un método propio .Siempre recomiendo usar los recursos comunes de Apache, ya que probablemente sea mejor que uno que pueda escribir por su cuenta. Además, puede hacer un trabajo 'real' en lugar de reinventar.
La clase que le interesa es el comparador nulo . Le permite hacer nulos altos o bajos. También le da su propio comparador para usar cuando los dos valores no son nulos.
En su caso, puede tener una variable miembro estática que haga la comparación y luego su
compareTo
método solo hace referencia a eso.Algo como
}
Incluso si decide rodar la suya, tenga en cuenta esta clase, ya que es muy útil al ordenar listas que contienen elementos nulos.
fuente
Sé que puede que no responda directamente a su pregunta, porque usted dijo que los valores nulos deben ser compatibles.
Pero solo quiero señalar que admitir nulos en compareTo no está en línea con el contrato compareTo descrito en javadocs oficiales para Comparable :
Por lo tanto, arrojaría NullPointerException explícitamente o simplemente dejaría que se lance la primera vez cuando se desreferencia el argumento nulo.
fuente
Puedes extraer el método:
}
fuente
Puede diseñar su clase para que sea inmutable (Effective Java 2nd Ed. Tiene una gran sección sobre esto, Elemento 15: Minimizar la mutabilidad) y asegurarse de que no haya valores nulos posibles (y use el patrón de objetos nulos si es necesario). Luego puede omitir todas esas comprobaciones y asumir con seguridad que los valores no son nulos.
fuente
Estaba buscando algo similar y esto parecía un poco complicado, así que hice esto. Creo que es un poco más fácil de entender. Puede usarlo como un Comparador o como un revestimiento. Para esta pregunta, cambiaría a compareToIgnoreCase (). Como es, los nulos flotan. Puedes voltear el 1, -1 si quieres que se hundan.
.
fuente
Podemos usar Java 8 para hacer una comparación nula entre objetos. supuse que tenía una clase Boy con 2 campos: nombre de cadena y edad entera y quiero comparar primero los nombres y luego las edades si ambos son iguales
y el resultado:
fuente
En caso de que alguien use Spring, hay una clase org.springframework.util.comparator.NullSafeComparator que también lo hace por usted. Simplemente decora tu propio comparable con él de esta manera
new NullSafeComparator<YourObject>(new YourComparable(), true)
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/comparator/NullSafeComparator.html
fuente
Para el caso específico en el que sabe que los datos no tendrán valores nulos (siempre es una buena idea para las cadenas) y los datos son realmente grandes, todavía está haciendo tres comparaciones antes de comparar realmente los valores, si sabe con certeza que este es su caso , Puedes optimizar un poco. YMMV como código legible supera la optimización menor:
fuente
la salida es
fuente
Una de las formas simples de usar NullSafe Comparator es usar la implementación de Spring de la misma, a continuación se muestra uno de los ejemplos simples para referirse:
fuente
Otro ejemplo de Apache ObjectUtils. Capaz de ordenar otros tipos de objetos.
fuente
Esta es mi implementación que uso para ordenar mi ArrayList. Las clases nulas se ordenan hasta el final.
para mi caso, EntityPhone extiende EntityAbstract y mi contenedor es List <EntityAbstract>.
El método "compareIfNull ()" se utiliza para la ordenación segura nula. Los otros métodos son para completar, mostrando cómo se puede comparar compareIfNull.
fuente
Si quieres un Hack simple:
si desea poner nulos al final de la lista, simplemente cambie esto en el método anterior
fuente