Pregunta

En Java, uso una clase en la que algunos campos pueden ser null. Por ejemplo:

class Foo {
    String bar;
    //....
}

Quiero escribir un BarComparator para esta clase,

    private static class BarComparator
            implements Comparator<Foo> {
        public int compare( final Foo o1, final Foo o2 )
        {
            // Implementation goes here
        }
    }

¿Existe una forma estándar de lidiar con el hecho de que cualquiera de o1, o2, o1.bar, o2.bar puede ser if, sin escribir muchos else ... <= anidados >?

¡Salud!

¿Fue útil?

Solución

Supongo que podría ajustar la llamada al método compareTo de campo con un pequeño método estático para ordenar nulos altos o bajos:

static <T extends Comparable<T>> int cp(T a, T b) {
     return
         a==null ?
         (b==null ? 0 : Integer.MIN_VALUE) :
         (b==null ? Integer.MAX_VALUE : a.compareTo(b));
}

Uso simple (múltiples campos es como lo haría normalmente):

public int compare( final Foo o1, final Foo o2 ) {
    return cp(o1.field, o2.field);
}

Otros consejos

¡Gracias por las respuestas! El método genérico y los comparadores de Google parecen interesantes.

Y descubrí que hay un NullComparator en las Apache Commons Collections (que estamos actualmente usando):

private static class BarComparator
        implements Comparator<Foo>
{
    public int compare( final Foo o1, final Foo o2 )
    {
        // o1.bar & o2.bar nulleness is taken care of by the NullComparator.
        // Easy to extend to more fields.
        return NULL_COMPARATOR.compare(o1.bar, o2.bar);
    }

    private final static NullComparator NULL_COMPARATOR =
                                            new NullComparator(false);
}

Nota: Me concentré en el campo bar aquí para mantenerlo al punto.

Depende de si considera que una entrada nula es un valor de cadena válido que vale la pena comparar. es nulo < o > " manzana " ;. Lo único que puedo decir con certeza es que nulo == nulo. Si puede definir dónde encaja nulo en el orden, puede escribir el código adecuadamente.

En este caso, podría elegir lanzar un NullPointerExcpetion o IllegalArgumentException e intentar manejar el nulo en un nivel superior al no ponerlo en la comparación en primer lugar.

Puedes escribir tu Comparador para ello. Digamos que tiene una persona de clase con el nombre de cadena como campo privado. Método getName () y setName () para acceder al nombre del campo. A continuación se muestra el comparador para la clase Persona.

    Collections.sort(list, new Comparator<Person>() {
        @Override
        public int compare(Person a, Person b) {
            if (a == null) {
                if (b == null) {
                    return 0;
                }
                return -1;
            } else if (b == null) {
                return 1;
            }
            return a.getName().compareTo(b.getName());
        }
    });

Actualización:

A partir de Java 8, puede usar las API de abajo para List.

// Push nulls at the end of List
Collections.sort(subjects1, Comparator.nullsLast(String::compareTo));

// Push nulls at the beginning of List
Collections.sort(subjects1, Comparator.nullsFirst(String::compareTo));

La clave aquí es averiguar cómo desea que se traten los nulos. Algunas opciones son: a) asumir nulos antes de todos los demás objetos en orden de clasificación b) asumir nulos después de todos los demás objetos en orden de clasificación c) tratar nulos como equivalentes a algún valor predeterminado d) tratar nulos como condiciones de error. El que elija dependerá completamente de la aplicación en la que esté trabajando.

En el último caso, por supuesto, lanzas una excepción. Para los demás, necesita un caso if / else de cuatro vías (aproximadamente tres minutos de codificación de uno ha resuelto lo que quiere que sean los resultados).

Si está utilizando colecciones de Google, puede encontrar el Comparadores clase útil. If tiene métodos auxiliares para ordenar nulos como los elementos mayores o menores de la colección. Puede usar comparadores compuestos para ayudar a reducir la cantidad de código.

También existe la clase org.springframework.util.comparator.NullSafeComparator en Spring Framework que puede usar.

Ejemplo (Java 8):

SortedSet<Foo> foos = new TreeSet<>( ( o1, o2 ) -> {
        return new NullSafeComparator<>( String::compareTo, true ).compare( o1.getBar(), o2.getBar() );
    } );

    foos.add( new Foo(null) );
    foos.add( new Foo("zzz") );
    foos.add( new Foo("aaa") );

    foos.stream().forEach( System.out::println );

Esto imprimirá:

Foo{bar='null'}
Foo{bar='aaa'}
Foo{bar='zzz'}

Me parece que no hay un método para hacerlo, pero de todos modos el código no es tan largo.

No debe usar el NullComparator de la manera en que lo hace: está creando una nueva instancia de la clase para cada operación de comparación, y si p. está ordenando una lista con 1000 entradas, que serán 1000 * log2 (1000) objetos que son completamente superfluos. Esto puede volverse problemático rápidamente.

O bien subclasifíquelo, delegue en él o simplemente implemente su propia verificación nula; en realidad no es tan complejo:

private static class BarComparator
        implements Comparator<Foo> {
    private NullComparator delegate = new NullComparator(false);

    public int compare( final Foo o1, final Foo o2 )
    {
        return delegate.compare(o1.bar, o2.bar);
    }
}

Considerar al cliente como un POJO. Mi respuesta sería:

Comparator<Customer> compareCustomer = Comparator.nullsLast((c1,c2) -> c1.getCustomerId().compareTo(c2.getCustomerId()));

O

Comparator<Customer> compareByName = Comparator.comparing(Customer::getName,nullsLast(String::compareTo));

Creo que las declaraciones de devolución anticipadas serían la otra alternativa a muchos ifs

por ejemplo

if(o1==null) return x;
if(o2==null) return x;
if(o1.getBar()==null) return x;
if(o2.getBar()==null) return x;

// No null checks needed from this point.
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top