2012-11-22 15 views
9

To pytanie zostało zadane wcześniej, ale nie znalazłem przyzwoitego wdrożenia z wyjaśnieniem.Java: null safe compareTo method

public int compareTo(Object o) 
{ 
    if (this == null || o == null) 
    { 
     return 0; 
    } 
    Tok tmp = (Tok) o;  
    if (this.rang < tmp.rang) 
    { 
     return -1; 
    } else if (this.rang > tmp.rang) { 
     return 1; 
    } else { 
     return 0; 
    } 
} 

Przeczytałem dwa podobne pytania, które jeszcze znalazłem; nalegają na wdrożenie innej metody. Nie rozumiem, dlaczego to nie powinno działać. Metoda pobiera dodatkowy obiekt i sprawdza, czy jest to poprawna instancja, czy też po prostu zwraca 0123,, po prostu zwraca 0; jaki byłby najłatwiejszy sposób implementacji bezpiecznego zerowania compareTo.

Realizacja że pracował dla mnie było:

public int compareTo(Object o) 
{ 
    if (o == null) 
    { 
     return 0; 
    } 
    Tok tmp = (Tok) o;  
    if (this.rang < tmp.rang) 
    { 
     return -1; 
    } else if (this.rang > tmp.rang) { 
     return 1; 
    } else { 
     return 0; 
    } 
} 

To nie jest optymalna realizacja jeden powinien wyglądać w co dobrzy ludzie napisali tutaj jako odpowiedzi. Dla mojego konkretnego przypadku było to wystarczająco przyzwoite, ponieważ nigdy nie jest puste, ale otrzymany obiekt może mieć wartość NULL, a początkowa implementacja stwierdza, że ​​albo ma zerowy wynik 0. Tak więc, jeśli dany obiekt ma wartość zerową, zwracane jest 0.

+12

'this' to ** nigdy **' null' w Javie. –

+0

@MattBall prawdopodobnie miał na myśli ** this.someInstance ** – PermGenError

+0

to nigdy nie może być nieważne –

Odpowiedz

12

Osobiście lubię Guava's Ordering dla porównywania wartości bezpiecznych. Możesz określić #nullsFirst() lub #nullsLast(), aby uniknąć s. NullPointerException.

Inne ważne notatki, głównie z komentarzy:

  • this jest nigdynull w Javie
  • Rozważ użycie Guava's ComparisonChain jeśli wdrożenie drobnoziarnisty compareTo()
  • Przy wdrażaniu Comparable , pamiętaj o podaniu parametru typu, aby uzyskać bezpieczeństwo w czasie kompilacji i nie trzeba używać instanceof lub odleć:

    class Tok implements Comparable<Tok> { 
        // snip 
    
        public int compareTo(Tok other) { 
         // snip 
        } 
    } 
    
5

Powracanie 0 oznacza, że ​​this i o są równe, co nie jest prawdą, jeśli o ma wartość null. Ponadto, this nigdy nie będzie mieć wartości NULL.

Oczywiście, zależy to od aplikacji. Możesz chcieć mieć obiekt, który powinien być równy zeru. To, do czego wrócisz, zależy od ciebie, ale jeśli szukasz ogólnej bezpiecznej metody, to nie jest to idealne rozwiązanie.

Aby być całkowicie generycznym, sprawdziłbym, czy o jest null, a jeśli tak, wyrzuć jakiś wyjątek.

+1

W szczególności wyjątek NullPointerException zgodnie z dokumentacją Comparable. http://docs.oracle.com/javase/6/docs/api/java/lang/Comparable.html – DPM

+0

Nie potrzebuję wyjątku Mam tablicę obiektów Tok [] i przekazuję ją do Arrays.sort() i uzyskać wyjątek Null. –

+0

Emmerioch ma rację, zaufaj nam, nie sprawdzaj wartości Null, zobacz moją odpowiedź poniżej, ale czyń swój kod czystym, usuwaj lub unikaj obiektów zerowych w Tok [], które chcesz posortować! Idź tą drogą, inaczej spędzasz dużo czasu szukając błędu, prawdopodobnie nie znajdziesz łatwo. – AlexWien

1

Porównując dwa przedmioty mogą być null bezpieczne jak każdej innej metody, połów tutaj jest to, że to normalna metoda ma dwa parametry ale compareTo otrzymuje jeden i drugi jest sam obiekt.

this NIGDY nie może być pusta, to znaczy, że wykonujesz kod w obiekcie null (bez instancji). W takim przypadku NullPointerException zostanie rzucony zaraz po wywołaniu compareTo, uniemożliwiając wykonanie kodu.

Istnieje tyle podejść, co obiektów, ponieważ porównanie może opierać się na polach klasy, która MOŻE mieć wartość NULL (przeznaczone czapki, aby wykluczyć typy pierwotne). Krótko mówiąc, twoje kontrole zerowe powinny obejmować obiekt otrzymany jako parametr w compareTo i użyte pola. Ponadto, jeśli masz instancję zewnętrzną, która ma pewną logikę (tj. Klasę narzędzi), powinieneś sprawdzić, czy ta instancja jest również pusta.

Na marginesie, wszystko, co zwrócisz, jeśli jakikolwiek zaangażowany obiekt ma wartość null, musi być spójne i udokumentowane (możesz zwrócić wartość -1 lub 1, aby wstawić wartości zerowe na początku lub na końcu). Wystarczy unikać powrocie 0 (który byłby taki sam przypadek jak gdyby equals powrócił true dla null obiektu.

1

dziwne, jak może się wydawać, ale to nie jest bezpieczne. Spróbuj dodać Tok do TreeSet lub TreeMap (jako klucz) Otrzymasz wyjątek NullPointerException.Problem polega na tym, że implementacja TreeSet jest oparta na TreeMap.Jeśli spróbujesz dodać (null), podstawowa mapa spróbuje umieścić swoją wartość null, co spowoduje NPE

+0

Kto mówi, że OP używa 'TreeSet' lub' TreeMap'? –

+0

@matt OP powiedział w jednym ze swoich komentarzy, używa sort(), który używa compateTo(). – AlexWien

+0

@AlexWien ... co konkretnie oznacza, że ​​OP jest _nie_ przy użyciu 'TreeSet' lub' TreeMap'. –

4

Nie jestem usatysfakcjonowany inne odpowiedzi:
NIE powinieneś sprawdzić wartości zerowej w porównaniu do
Wymagane jest zgłoszenie wyjątku NullPointerException. do twoich Drzew i mają trudności ze znalezieniem przyczyny, dla której twoja TreeMap nie działa.

Bardzo godne polecenia metoda:

public int compareTo(Tok other) { 
    int thisRang = this.rang; 
    int otherRang = other.rang; 
    return (thisRang < otherRang ? -1 : (thisRang == otherRang ? 0 : 1)); 
    } 
    public int compareTo(Object other) { 
    return compareTo((Tok)other); 
    } 

Dalsze aby uczynić go idealnym klasa Tok powinien być końcowy! (W przeciwnym razie można mieć problemy gdy podklasy z Tok. (Sun popełnił ten błąd w klasie Date)

final class Tok { 
    int rang; 
} 

Zajmowanie się porównać i wynosi nie zawsze jest łatwe, należy rozważyć użycie zamiast drzew (TreeMap) HashMap, wtedy nie trzeba wdrożyć compareTo. powinny implementować hashCode, gdzie można po prostu wrócić this.rang.

koniec jego jest wysoce zalecana, ale nie obowiązkowe zaimplementować equals()

public boolean equals(Object obj) { 
return obj instanceof Tok 
    && this.rang() == ((Tok) obj).rang; 
} 
+0

umieszczenie tablicy obiektów Tok w pliku Arrays.sort (tok []) nadal daje wyjątek. –

+0

Tak, który wyjątek? Nullpointer? To jest dobre! Nie powinieneś mieć obiektu zerowego w Tok []. Upewnij się, że nie dodasz null objectr do Tok [] – AlexWien

+0

Ale to jest problem i wiem, że mogę skrócić mój Tok [], aby nie zawierał żadnych zerowych indeksów, ale oznacza to, że muszę stworzyć kolejną tablicę, policzyć liczbę nie zerowych indeksów, a następnie skopiować ta tablica do tymczasowej tablicy i posortuj ją.To jest po prostu skomplikowane, po prostu chcę umieścić wszystkie elementy zerowe w największej i umieścić je na końcu. –

1

aUT hor twierdzi, że nie chce usuwać wartości pustych z jego Tok [].
Oto soultion który pozwala uporządkować z wartościami NULL i nie narusza umów java

Aby tego uniknąć, należy utworzyć compareTo wewnątrz klasy Tok że narusza umowę compareTo, Ci stworzyć wyraźną NullSafeComparator:

/** 
* This comparator accepts null objects, 
* sorts ascending, null values are after non null values. 
*/ 
public static final class NullSafeComparator implements Comparator<Tok> { 
    public int compare(Tok o1, Tok o2) { 
     int r1 = Integer.MAX_VALUE; 
     int r2 = Integer.MAX_VALUE; 
     if (o1 != null) { 
      r1 = o1.rang; 
     } 
     if (o2 != null) { 
      r2 = o2.rang; 
     } 
     return (r1 < r2 ? -1 : (r1 == r2 ? 0 : 1)); 
    } 
} 

uproszczona klasa Tok (usunąć słowa kluczowego static jej jest używany do określenia, że ​​wszystko wewnątrz jednej jednostki klasy test):

public static class Tok { 
    int rang; 
    public Tok(int rang) { 
     this.rang = rang; 
    } 
    public String toString() { 
     return Integer.toString(rang); 
    } 
} 

wreszcie un to przetestować, aby pokazać:

public void testSort() { 

    Tok[] toks = new Tok[5]; 
    toks[0] = new Tok(3); 
    toks[1] = new Tok(1); 
    toks[2] = null; 
    toks[3] = null; 
    toks[4] = new Tok(2); 



    Arrays.sort(toks, new NullSafeComparator()); 



    for (Tok tok: toks) { 
     System.out.println(tok); 
    } 
    assertEquals(1, toks[0]); 
    assertNull(toks[4]); 
} 

które dadzą następujące pożądany rezultat:

1 
2 
3 
null 
null 
+2

Chill out. "Komparator" może mieć zerowy poziom bezpieczeństwa; [JavaDoc wyraźnie tak mówi] (http://docs.oracle.com/javase/7/docs/api/java/util/Comparator.html): _ "W przeciwieństwie do' Comparable', komparator może opcjonalnie umożliwić porównanie null argumenty, przy zachowaniu wymagań dla relacji równoważności. "_ –

+0

Ok, usunę Dirty z kodu – AlexWien

0

Według documentation:

Note that null is not an instance of any class, and e.compareTo(null) should 
throw a NullPointerException even though e.equals(null) returns false. 

więc jeśli wdrożyć metodę zerową bezpieczny, jego zachowanie będzie nieoczekiwane (aka niezgodne z dokumentacją i prawdopodobnie z resztą API).