Jak uprościć implementację funkcji compareTo () z bezpieczną wartością null?

156

Implementuję compareTo()metodę dla prostej klasy, takiej jak ta (aby móc korzystać z Collections.sort()innych dobrodziejstw oferowanych przez platformę Java):

public class Metadata implements Comparable<Metadata> {
    private String name;
    private String value;

// Imagine basic constructor and accessors here
// Irrelevant parts omitted
}

Chcę, aby te obiekty były uporządkowane w sposób naturalny : 1) posortowane według nazwy i 2) posortowane według wartości, jeśli nazwa jest taka sama; w obu porównaniach nie należy rozróżniać wielkości liter. Dla obu pól wartości null są całkowicie akceptowalne, więc compareTow takich przypadkach nie wolno ich łamać.

Rozwiązanie, które przychodzi na myśl, wygląda następująco (używam tutaj „klauzul ochronnych”, podczas gdy inni mogą preferować pojedynczy punkt powrotu, ale to nie ma znaczenia):

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

To działa, ale nie jestem do końca zadowolony z tego kodu. Wprawdzie nie jest to zbyt skomplikowane, ale jest dość rozwlekłe i żmudne.

Pytanie brzmi, jak uczynić to mniej szczegółowym (zachowując funkcjonalność)? Jeśli pomogą, możesz odwołać się do standardowych bibliotek Java lub Apache Commons. Czy jedyną opcją, aby to (trochę) uprościć, byłoby zaimplementowanie własnego „NullSafeStringComparator” i zastosowanie go do porównania obu pól?

Edycje 1-3 : Eddie ma rację; naprawiono powyższy przypadek "obie nazwy są puste"

O przyjętej odpowiedzi

Zadałem to pytanie w 2009 roku, oczywiście na Javie 1.6, a wtedy czyste rozwiązanie JDK autorstwa Eddiego było moją preferowaną akceptowaną odpowiedzią. Nigdy nie udało mi się tego zmienić aż do teraz (2017).

Istnieją również rozwiązania biblioteczne innych firm - 2009 Apache Commons Collections i 2013 Guava, oba opublikowane przeze mnie - które wolałem w pewnym momencie.

Teraz zaakceptowałem odpowiedź z czystego rozwiązania Java 8 autorstwa Łukasza Wiktora . Powinno to być zdecydowanie preferowane, jeśli jest to Java 8, a obecnie Java 8 powinna być dostępna dla prawie wszystkich projektów.

Jonik
źródło
stackoverflow.com/questions/369383/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Odpowiedzi:

173

Korzystanie z języka Java 8 :

private static Comparator<String> nullSafeStringComparator = Comparator
        .nullsFirst(String::compareToIgnoreCase); 

private static Comparator<Metadata> metadataComparator = Comparator
        .comparing(Metadata::getName, nullSafeStringComparator)
        .thenComparing(Metadata::getValue, nullSafeStringComparator);

public int compareTo(Metadata that) {
    return metadataComparator.compare(this, that);
}
Łukasz Wiktor
źródło
7
Popieram używanie wbudowanych elementów Java 8 na korzyść Apache Commons Lang, ale ten kod Java 8 jest dość brzydki i wciąż rozwlekły. Na razie zostanę przy org.apache.commons.lang3.builder.CompareToBuilder.
jschreiner
1
Nie działa to w przypadku Collections.sort (Arrays.asList (null, val1, null, val2, null)), ponieważ spróbuje wywołać funkcję compareTo () na obiekcie o wartości null. Szczerze mówiąc, wygląda na problem ze strukturą kolekcji, próbując dowiedzieć się, jak to załatwić.
Pedro Borges,
3
@PedroBorges Autor zapytał o sortowanie obiektów kontenerów, które posiadają pola sortowalne (gdzie te pola mogą być puste), a nie o sortowanie zerowych odwołań do kontenerów. Tak więc, chociaż twój komentarz jest poprawny, Collections.sort(List)to nie działa, gdy lista zawiera wartości null, komentarz nie dotyczy pytania.
Scrubbie
211

Możesz po prostu użyć Apache Commons Lang :

result = ObjectUtils.compare(firstComparable, secondComparable)
Dag
źródło
4
(@Kong: Dba o bezpieczeństwo zerowe, ale nie uwzględnia wielkości liter, co było kolejnym aspektem pierwotnego pytania. Dlatego nie zmienia zaakceptowanej odpowiedzi.)
Jonik
3
Moim zdaniem Apache Commons nie powinno być akceptowaną odpowiedzią w 2013 r. (Nawet jeśli niektóre podprojekty są lepiej utrzymywane niż inne). Guava można wykorzystać do osiągnięcia tego samego ; zobacz nullsFirst()/ nullsLast().
Jonik,
8
@Jonik Dlaczego uważasz, że Apache Commons nie powinno być akceptowaną odpowiedzią w 2013 roku?
naprawdęnice
1
Duże części Apache Commons to rzeczy starsze / źle utrzymane / niskiej jakości. Istnieją lepsze alternatywy dla większości rzeczy, które zapewnia, na przykład w Guava, która jest biblioteką bardzo wysokiej jakości w całym tekście, i coraz częściej w samym JDK. Około 2005 roku tak, Apache Commons było gównem, ale obecnie nie ma takiej potrzeby w większości projektów. (Jasne, są wyjątki; na przykład gdybym z jakiegoś powodu potrzebował klienta FTP, prawdopodobnie
użyłbym
6
@Jonik, jak odpowiedziałbyś na pytanie, używając guawy? Twoje twierdzenie, że Apache Commons Lang (pakiet org.apache.commons.lang3) jest „starsze / źle utrzymane / niskiej jakości” jest fałszywe lub w najlepszym przypadku bezpodstawne. Commons Lang3 jest łatwe do zrozumienia i użytkowania oraz jest aktywnie utrzymywane. Jest to prawdopodobnie moja najczęściej używana biblioteka (poza Spring Framework i Spring Security) - na przykład klasa StringUtils z metodami bezpiecznymi dla wartości null sprawia, że ​​normalizacja wejścia jest banalna.
Paul
93

Zaimplementowałbym zerowy bezpieczny komparator. Może istnieć implementacja, ale jest to tak proste do zaimplementowania, że ​​zawsze tworzyłem własną.

Uwaga: powyższy komparator, jeśli obie nazwy są puste, nie porówna nawet pól wartości. Nie sądzę, że tego chcesz.

Zaimplementowałbym to za pomocą czegoś takiego:

// primarily by name, secondarily by value; null-safe; case-insensitive
public int compareTo(final Metadata other) {

    if (other == null) {
        throw new NullPointerException();
    }

    int result = nullSafeStringComparator(this.name, other.name);
    if (result != 0) {
        return result;
    }

    return nullSafeStringComparator(this.value, other.value);
}

public static int nullSafeStringComparator(final String one, final String two) {
    if (one == null ^ two == null) {
        return (one == null) ? -1 : 1;
    }

    if (one == null && two == null) {
        return 0;
    }

    return one.compareToIgnoreCase(two);
}

EDYCJA: Poprawiono literówki w próbce kodu. Właśnie to dostaję, nie testując go najpierw!

EDYCJA: Promowany nullSafeStringComparator do statycznego.

Eddie
źródło
2
Jeśli chodzi o zagnieżdżone „if” ... Uważam, że zagnieżdżone if jest mniej czytelne w tym przypadku, więc go unikam. Tak, czasami będzie z tego powodu niepotrzebne porównanie. ostateczne parametry nie są konieczne, ale to dobry pomysł.
Eddie
31
Słodkie użycie XOR
James McMahon
9
@phihag - wiem, że minęły już 3 lata, ALE ... finalsłowo kluczowe nie jest naprawdę potrzebne (kod Java jest już gadatliwy). Jednak zapobiega ponownemu wykorzystywaniu parametrów jako lokalnych zmiennych (okropna praktyka kodowania). nasze zbiorowe rozumienie oprogramowania staje się lepsze z biegiem czasu, wiemy, że domyślnie rzeczy powinny być ostateczne / stałe / niezmienne. Więc wolę trochę więcej gadatliwości w używaniu finalw deklaracjach parametrów (jakkolwiek trywialna może być ta funkcja) do uzyskania inmutability-by-quasi-default.) Narzut związany z zrozumiałością / utrzymywalnością jest pomijalny w ogólnym schemacie rzeczy.
luis.espinal
24
@James McMahon Muszę się nie zgodzić. Xor (^) można po prostu zastąpić nierównym (! =). Kompiluje się nawet do tego samego kodu bajtowego. Użycie! = Vs ^ to tylko kwestia gustu i czytelności. Więc sądząc po tym, że byłeś zaskoczony, powiedziałbym, że to nie pasuje tutaj. Użyj xor, gdy próbujesz obliczyć sumę kontrolną. W większości innych przypadków (takich jak ten) trzymajmy się! =.
bvdb
5
Łatwo jest rozszerzyć tę odpowiedź, zastępując String przez T, T zadeklarowane jako <T extends Comparable <T>> ... i wtedy możemy bezpiecznie porównać dowolne obiekty porównywalne do wartości zerowej
Thierry
20

Zobacz dolną część tej odpowiedzi dla zaktualizowanego (2013) rozwiązania używającego guawy.


Z tym ostatecznie poszedłem. Okazało się, że mamy już metodę narzędziową do porównywania ciągów znaków null-safe, więc najprostszym rozwiązaniem było skorzystanie z tego. (To duża baza kodów; łatwo przeoczyć takie rzeczy :)

public int compareTo(Metadata other) {
    int result = StringUtils.compare(this.getName(), other.getName(), true);
    if (result != 0) {
        return result;
    }
    return StringUtils.compare(this.getValue(), other.getValue(), true);
}

Oto jak jest zdefiniowany helper (jest przeciążony, więc możesz również zdefiniować, czy wartości null są pierwsze czy ostatnie, jeśli chcesz):

public static int compare(String s1, String s2, boolean ignoreCase) { ... }

Jest to więc zasadniczo to samo, co odpowiedź Eddiego (chociaż statycznej metody pomocniczej nie nazwałbym komparatorem ), a także uzhin .

W każdym razie, ogólnie rzecz biorąc, zdecydowanie wolałbym rozwiązanie Patricka , ponieważ uważam, że dobrą praktyką jest korzystanie z istniejących bibliotek, gdy tylko jest to możliwe. ( Poznaj i korzystaj z bibliotek, jak mówi Josh Bloch.) Ale w tym przypadku nie dałoby to najczystszego, najprostszego kodu.

Edycja (2009): wersja Apache Commons Collections

Właściwie, oto sposób na NullComparatoruproszczenie rozwiązania opartego na Apache Commons . Połącz to z rozróżnianiem wielkości literComparator w Stringklasie:

public static final Comparator<String> NULL_SAFE_COMPARATOR 
    = new NullComparator(String.CASE_INSENSITIVE_ORDER);

@Override
public int compareTo(Metadata other) {
    int result = NULL_SAFE_COMPARATOR.compare(this.name, other.name);
    if (result != 0) {
        return result;
    }
    return NULL_SAFE_COMPARATOR.compare(this.value, other.value);
}

Myślę, że to całkiem eleganckie. (Pozostaje tylko jeden mały problem: Commons NullComparatornie obsługuje typów ogólnych, więc istnieje niezaznaczone przypisanie).

Aktualizacja (2013): wersja Guava

Prawie 5 lat później, oto jak poradziłbym sobie z moim pierwotnym pytaniem. Jeśli koduję w Javie, użyłbym (oczywiście) guawy . (I na pewno nie Apache Commons).

Umieść tę stałą gdzieś, np. W klasie „StringUtils”:

public static final Ordering<String> CASE_INSENSITIVE_NULL_SAFE_ORDER =
    Ordering.from(String.CASE_INSENSITIVE_ORDER).nullsLast(); // or nullsFirst()

Następnie w public class Metadata implements Comparable<Metadata>:

@Override
public int compareTo(Metadata other) {
    int result = CASE_INSENSITIVE_NULL_SAFE_ORDER.compare(this.name, other.name);
    if (result != 0) {
        return result;
    }
    return CASE_INSENSITIVE_NULL_SAFE_ORDER.compare(this.value, other.value);
}    

Oczywiście jest to prawie identyczne z wersją Apache Commons (obie używają CASE_INSENSITIVE_ORDER JDK ), ponieważ nullsLast()jest to jedyna rzecz specyficzna dla guawy. Ta wersja jest lepsza po prostu dlatego, że Guava jest lepsza, jako zależność, od Commons Collections. (Jak wszyscy się zgadzają .)

Jeśli się zastanawiałeś Ordering, zwróć uwagę, że implementuje Comparator. Jest to bardzo przydatne, szczególnie w przypadku bardziej złożonych potrzeb związanych z sortowaniem, pozwalając na przykład połączyć kilka Zamówień za pomocą compound(). Przeczytaj wyjaśnienie dotyczące zamawiania, aby uzyskać więcej informacji!

Jonik
źródło
2
String.CASE_INSENSITIVE_ORDER naprawdę sprawia, że ​​rozwiązanie jest znacznie czystsze. Niezła aktualizacja.
Patrick
2
Jeśli i tak używasz Apache Commons, istnieje, ComparatorChainwięc nie potrzebujesz własnej compareTometody.
ameba
13

Zawsze polecam korzystanie ze wspólnych zasobów Apache, ponieważ najprawdopodobniej będzie to lepsze niż takie, które możesz pisać samodzielnie. Dodatkowo możesz wtedy wykonywać „prawdziwą” pracę, zamiast wymyślać na nowo.

Klasa, którą jesteś zainteresowany, to komparator wartości zerowych . Pozwala tworzyć wartości zerowe wysokie lub niskie. Dajesz mu również swój własny komparator, który ma być używany, gdy dwie wartości nie są zerowe.

W twoim przypadku możesz mieć statyczną zmienną składową, która dokonuje porównania, a następnie twoja compareTometoda odwołuje się do niej.

Coś jak

class Metadata implements Comparable<Metadata> {
private String name;
private String value;

static NullComparator nullAndCaseInsensitveComparator = new NullComparator(
        new Comparator<String>() {

            @Override
            public int compare(String o1, String o2) {
                // inputs can't be null
                return o1.compareToIgnoreCase(o2);
            }

        });

@Override
public int compareTo(Metadata other) {
    if (other == null) {
        return 1;
    }
    int res = nullAndCaseInsensitveComparator.compare(name, other.name);
    if (res != 0)
        return res;

    return nullAndCaseInsensitveComparator.compare(value, other.value);
}

}

Nawet jeśli zdecydujesz się wyrzucić własną, pamiętaj o tej klasie, ponieważ jest bardzo przydatna podczas zamawiania list, które zawierają elementy puste.

Patrick
źródło
Dzięki, miałem nadzieję, że coś takiego znajdzie się w Commons! W tym przypadku jednak nie skończyłem z niego korzystać: stackoverflow.com/questions/481813/…
Jonik
Właśnie zdałem sobie sprawę, że Twoje podejście można uprościć, używając String.CASE_INSENSITIVE_ORDER; zobacz moją edytowaną odpowiedź uzupełniającą.
Jonik
To jest dobre, ale nie powinno tam być sprawdzenia "if (other == null) {". Dokument Javadoc for Comparable mówi, że compareTo powinno zgłosić wyjątek NullPointerException, jeśli other jest null.
Daniel Alexiuc
7

Wiem, że może to nie być bezpośrednią odpowiedzią na twoje pytanie, ponieważ powiedziałeś, że muszą być obsługiwane wartości zerowe.

Ale chcę tylko zauważyć, że obsługa wartości null w funkcji compareTo nie jest zgodna z umową compareTo opisaną w oficjalnych javadocs dla Comparable :

Należy zauważyć, że null nie jest wystąpieniem żadnej klasy, a e.compareTo (null) powinien zgłosić wyjątek NullPointerException, mimo że e.equals (null) zwraca false.

Więc albo wyrzuciłbym jawnie NullPointerException, albo po prostu pozwoliłbym, aby został wyrzucony po raz pierwszy, gdy dereferencjonowany jest argument o wartości null.

Piotr Sobczyk
źródło
4

Możesz wyodrębnić metodę:

public int cmp(String txt, String otherTxt)
{
    if ( txt == null )
        return otjerTxt == null ? 0 : 1;

    if ( otherTxt == null )
          return 1;

    return txt.compareToIgnoreCase(otherTxt);
}

public int compareTo(Metadata other) {
   int result = cmp( name, other.name); 
   if ( result != 0 )  return result;
   return cmp( value, other.value); 

}

Yoni Roit
źródło
2
Czy „0: 1” nie powinno być „0: -1”?
Rolf Kristensen,
3

Możesz zaprojektować swoją klasę tak, aby była niezmienna (Effective Java 2nd Ed. Ma świetną sekcję na ten temat, Punkt 15: Minimalizuj zmienność) i upewnić się podczas konstrukcji, że żadne wartości null nie są możliwe (i użyj wzorca obiektu zerowego, jeśli to konieczne). Następnie możesz pominąć wszystkie te sprawdzenia i bezpiecznie założyć, że wartości nie są zerowe.

Fabian Steeg
źródło
Tak, to generalnie dobre rozwiązanie i upraszcza wiele rzeczy - ale tutaj bardziej interesował mnie przypadek, w którym wartości null są dozwolone z tego czy innego powodu i należy je wziąć pod uwagę :)
Jonik
2

Szukałem czegoś podobnego i wydawało mi się to trochę skomplikowane, więc zrobiłem to. Myślę, że jest to trochę łatwiejsze do zrozumienia. Możesz go używać jako komparatora lub jako pojedynczej wkładki. W przypadku tego pytania należy zmienić na compareToIgnoreCase (). Tak jak jest, unoszą się wartości zerowe. Możesz odwrócić 1, -1, jeśli chcesz, aby zatonęły.

StringUtil.NULL_SAFE_COMPARATOR.compare(getName(), o.getName());

.

public class StringUtil {
    public static final Comparator<String> NULL_SAFE_COMPARATOR = new Comparator<String>() {

        @Override
        public int compare(final String s1, final String s2) {
            if (s1 == s2) {
                //Nulls or exact equality
                return 0;
            } else if (s1 == null) {
                //s1 null and s2 not null, so s1 less
                return -1;
            } else if (s2 == null) {
                //s2 null and s1 not null, so s1 greater
                return 1;
            } else {
                return s1.compareTo(s2);
            }
        }
    }; 

    public static void main(String args[]) {
        final ArrayList<String> list = new ArrayList<String>(Arrays.asList(new String[]{"qad", "bad", "sad", null, "had"}));
        Collections.sort(list, NULL_SAFE_COMPARATOR);

        System.out.println(list);
    }
}
Dustin
źródło
2

możemy użyć java 8, aby dokonać przyjaznego zeru porównania między obiektem. przypuszczam, że mam klasę Boy z 2 polami: nazwa łańcucha i całkowity wiek i chcę najpierw porównać imiona, a następnie wiek, jeśli oba są równe.

static void test2() {
    List<Boy> list = new ArrayList<>();
    list.add(new Boy("Peter", null));
    list.add(new Boy("Tom", 24));
    list.add(new Boy("Peter", 20));
    list.add(new Boy("Peter", 23));
    list.add(new Boy("Peter", 18));
    list.add(new Boy(null, 19));
    list.add(new Boy(null, 12));
    list.add(new Boy(null, 24));
    list.add(new Boy("Peter", null));
    list.add(new Boy(null, 21));
    list.add(new Boy("John", 30));

    List<Boy> list2 = list.stream()
            .sorted(comparing(Boy::getName, 
                        nullsLast(naturalOrder()))
                   .thenComparing(Boy::getAge, 
                        nullsLast(naturalOrder())))
            .collect(toList());
    list2.stream().forEach(System.out::println);

}

private static class Boy {
    private String name;
    private Integer age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public Boy(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String toString() {
        return "name: " + name + " age: " + age;
    }
}

a wynik:

    name: John age: 30
    name: Peter age: 18
    name: Peter age: 20
    name: Peter age: 23
    name: Peter age: null
    name: Peter age: null
    name: Tom age: 24
    name: null age: 12
    name: null age: 19
    name: null age: 21
    name: null age: 24
Leo Ng
źródło
1

W konkretnym przypadku, w którym wiesz, że dane nie będą miały wartości null (zawsze jest to dobry pomysł w przypadku ciągów znaków), a dane są naprawdę duże, nadal wykonujesz trzy porównania przed faktycznym porównaniem wartości, jeśli wiesz na pewno, że tak jest w Twoim przypadku , możesz trochę zoptymalizować. YMMV jako czytelny kod przewyższa drobną optymalizację:

        if(o1.name != null && o2.name != null){
            return o1.name.compareToIgnoreCase(o2.name);
        }
        // at least one is null
        return (o1.name == o2.name) ? 0 : (o1.name != null ? 1 : -1);
kisna
źródło
1
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Comparator;

public class TestClass {

    public static void main(String[] args) {

        Student s1 = new Student("1","Nikhil");
        Student s2 = new Student("1","*");
        Student s3 = new Student("1",null);
        Student s11 = new Student("2","Nikhil");
        Student s12 = new Student("2","*");
        Student s13 = new Student("2",null);
        List<Student> list = new ArrayList<Student>();
        list.add(s1);
        list.add(s2);
        list.add(s3);
        list.add(s11);
        list.add(s12);
        list.add(s13);

        list.sort(Comparator.comparing(Student::getName,Comparator.nullsLast(Comparator.naturalOrder())));

        for (Iterator iterator = list.iterator(); iterator.hasNext();) {
            Student student = (Student) iterator.next();
            System.out.println(student);
        }


    }

}

wyjście jest

Student [name=*, id=1]
Student [name=*, id=2]
Student [name=Nikhil, id=1]
Student [name=Nikhil, id=2]
Student [name=null, id=1]
Student [name=null, id=2]
Nikhil Kumar K.
źródło
1

Jednym z prostych sposobów korzystania z komparatora NullSafe jest użycie implementacji Springa, poniżej znajduje się jeden z prostych przykładów do odniesienia:

public int compare(Object o1, Object o2) {
        ValidationMessage m1 = (ValidationMessage) o1;
        ValidationMessage m2 = (ValidationMessage) o2;
        int c;
        if (m1.getTimestamp() == m2.getTimestamp()) {
            c = NullSafeComparator.NULLS_HIGH.compare(m1.getProperty(), m2.getProperty());
            if (c == 0) {
                c = m1.getSeverity().compareTo(m2.getSeverity());
                if (c == 0) {
                    c = m1.getMessage().compareTo(m2.getMessage());
                }
            }
        }
        else {
            c = (m1.getTimestamp() > m2.getTimestamp()) ? -1 : 1;
        }
        return c;
    }
Amandeep Singh
źródło
0

Kolejny przykład Apache ObjectUtils. Potrafi sortować inne typy obiektów.

@Override
public int compare(Object o1, Object o2) {
    String s1 = ObjectUtils.toString(o1);
    String s2 = ObjectUtils.toString(o2);
    return s1.toLowerCase().compareTo(s2.toLowerCase());
}
snp0k
źródło
0

To jest moja implementacja, której używam do sortowania mojej ArrayList. klasy puste są sortowane do ostatniej.

w moim przypadku EntityPhone rozszerza EntityAbstract, a mój kontener to List <EntityAbstract>.

metoda "compareIfNull ()" jest używana do sortowania z zabezpieczeniem zerowym. Inne metody służą do określania kompletności, pokazując, w jaki sposób można użyć compareIfNull.

@Nullable
private static Integer compareIfNull(EntityPhone ep1, EntityPhone ep2) {

    if (ep1 == null || ep2 == null) {
        if (ep1 == ep2) {
            return 0;
        }
        return ep1 == null ? -1 : 1;
    }
    return null;
}

private static final Comparator<EntityAbstract> AbsComparatorByName = = new Comparator<EntityAbstract>() {
    @Override
    public int compare(EntityAbstract ea1, EntityAbstract ea2) {

    //sort type Phone first.
    EntityPhone ep1 = getEntityPhone(ea1);
    EntityPhone ep2 = getEntityPhone(ea2);

    //null compare
    Integer x = compareIfNull(ep1, ep2);
    if (x != null) return x;

    String name1 = ep1.getName().toUpperCase();
    String name2 = ep2.getName().toUpperCase();

    return name1.compareTo(name2);
}
}


private static EntityPhone getEntityPhone(EntityAbstract ea) { 
    return (ea != null && ea.getClass() == EntityPhone.class) ?
            (EntityPhone) ea : null;
}
Angel Koh
źródło
0

Jeśli chcesz prostego hacka:

arrlist.sort((o1, o2) -> {
    if (o1.getName() == null) o1.setName("");
    if (o2.getName() == null) o2.setName("");

    return o1.getName().compareTo(o2.getName());
})

jeśli chcesz wstawić wartości null na koniec listy, po prostu zmień to w powyższej metodzie

return o2.getName().compareTo(o1.getName());
MarsLudzie
źródło