Domanda

In Java, utilizzo una classe in cui alcuni campi possono essere null. Ad esempio:

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

Voglio scrivere un BarComparator per questa classe,

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

Esiste un modo standard per gestire il fatto che uno qualsiasi di o1, o2, o1.bar, o2.bar può essere if, senza scrivere molti nidificati else ... <= >?

Cheers!

È stato utile?

Soluzione

Immagino che potresti avvolgere la chiamata sul campo compareTo method con un piccolo metodo statico per ordinare i null alti o bassi:

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));
}

Utilizzo semplice (più campi è come si farebbe normalmente):

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

Altri suggerimenti

Grazie per le risposte! Il metodo generico e i comparatori di Google sembrano interessanti.

E ho scoperto che esiste un NullComparator nella Collezioni Apache Commons (di cui siamo attualmente in uso):

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: mi sono concentrato sul campo bar qui per mantenerlo al punto.

Dipende se si considera che una voce null sia un valore stringa valido che valga la pena confrontare. è null < o > & Quot; & Mela quot ;. L'unica cosa che posso dire con certezza è che null == null. Se riesci a definire dove null rientra nell'ordinamento, puoi scrivere il codice in modo appropriato.

In questo caso potrei scegliere di lanciare una NullPointerExcpetion o IllegalArgumentException e provare a gestire il null a un livello superiore non inserendolo nel confronto in primo luogo.

Puoi scrivere il tuo comparatore per questo. Supponiamo che tu abbia una classe Persona con nome stringa come campo privato. Metodo getName () e setName () per accedere al nome del campo. Di seguito è riportato il comparatore per la classe Person.

    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());
        }
    });

Aggiornamento:

A partire da Java 8 è possibile utilizzare le API di seguito per Elenco.

// 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 cosa chiave qui è capire come vorresti che i null fossero trattati. Alcune opzioni sono: a) presuppone che i null vengano prima di tutti gli altri oggetti nell'ordinamento b) assumano che i null vengano dopo tutti gli altri oggetti nell'ordinamento c) trattano null come equivalente a un valore predefinito d) trattano i null come condizioni di errore. Quale sceglierai dipenderà interamente dall'applicazione su cui stai lavorando.

Nell'ultimo caso ovviamente lanci un'eccezione. Per gli altri hai bisogno di un caso if / else a quattro vie (circa tre minuti di codifica uno hai capito cosa vuoi che siano i risultati).

Se utilizzi raccolte Google, potresti trovare Comparatori classe utile. If ha metodi di supporto per ordinare i null come elementi più grandi o minimi della raccolta. Puoi utilizzare comparatori composti per aiutare a ridurre la quantità di codice.

C'è anche la classe org.springframework.util.comparator.NullSafeComparator in Spring Framework che puoi usare.

Esempio (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 );

Questo stamperà:

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

Mi sembra che non ci sia un metodo per farlo, ma comunque il codice non è così lungo.

Non dovresti usare NullComparator come fai tu: stai creando una nuova istanza della classe per ogni operazione di confronto, e se ad es. stai ordinando un elenco con 1000 voci, che saranno 1000 * log2 (1000) oggetti completamente superflui. Questo può diventare rapidamente problematico.

Sottoclassalo o delegalo o semplicemente implementa il tuo controllo null - non è poi così complesso:

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);
    }
}

Considerando il cliente come POJO, la mia risposta sarebbe:

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

o

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

Penso che le dichiarazioni di rimpatrio anticipate sarebbero l'altra alternativa a molti if

per es.

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.
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top