Java: null safe compareTo method

20,345

Solution 1

Personally, I like Guava's Ordering for null-safe comparing. You can specify #nullsFirst() or #nullsLast() to avoid NullPointerExceptions.

Other important notes, mostly from the comments:

  • this is never null in Java
  • Consider using Guava's ComparisonChain if you're implementing a fine-grained compareTo()
  • When implementing Comparable, be sure to specify the type parameter so you get compile-time type safety and don't have to use instanceof or casts:

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

Solution 2

Returning 0 would imply that this and o are equal, which isn't true if o is null. Also, this will never be null.

It's application-dependent, of course. You may want to have an object that should be equal to null. What you return there is up to you, but if you're looking for a general null safe method, it isn't really ideal.

To be completely generic, I would check if o is null and, if so, throw some sort of Exception.

Solution 3

I am not satisified with the other answers:
You should NOT check for null in compareTo.
It is requested that it throws a NullPointerException, otheriwse you will mess up your Trees and have difficulties finding why your TreeMap does not work.

A very recommendable method:

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

Further to make it perfect class Tok should be final! (Otherwise you could have problems when you subclass from Tok. (Sun made that Error in Class Date)

final class Tok {
    int rang;
}

Dealing with compare and equals is not always easy, consider using instead of Trees (TreeMap) a HashMap, then you dont have to implement compareTo. You should implement hashCode, where you simply return this.rang.

Finally its is highly recomended, but not mandatory to implement equals()

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

Solution 4

Strange as it may seem but it is not safe. Try to add your Tok to TreeSet or TreeMap (as a key) and you will get a NullPointerException. The problem is that TreeSet imlementation is based on TreeMap. When you will try to add(null) underlying map will try to put your null which will result in NPE

Solution 5

The author is insisting that he does not want to remove null values from his Tok[].
Here is a soultion that allows to sort with NULL values, and is not violating java contracts

To avoid that, you create a compareTo inside class Tok that violates the compareTo contract, you create an explicit 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));
    }
}

simplified class Tok (remove the static keyword its is used to define that all inside one unit test class):

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

Finally a unit test to show:

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

Which will give the following desired result:

1
2
3
null
null
Share:
20,345
Sterling Duchess
Author by

Sterling Duchess

Updated on June 30, 2020

Comments

  • Sterling Duchess
    Sterling Duchess almost 4 years

    It has been asked before, but I have not found a decent implementation with an explanation.

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

    I read two similar questions that I found yet; they insist on implementing another method. I do not understand why this should not work. The method gets an extra object and it checks if its a valid instance or null, if null simply return 0; what would be the easiest way to implement null-safe compareTo.

    The implementation that worked for me was:

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

    It's not the optimal implementation one should look in to what good people posted here as answers. For my particular case this was decent enough as this is never null yet the object received can be null and the initial implementation states if either is null return 0. So if given object is null 0 is returned.

  • DPM
    DPM over 11 years
    Specifically a NullPointerException according to Comparable's documentation. docs.oracle.com/javase/6/docs/api/java/lang/Comparable.html
  • Sterling Duchess
    Sterling Duchess over 11 years
    putting array of Tok objects in to Arrays.sort(tok[]) still yields an exception.
  • Sterling Duchess
    Sterling Duchess over 11 years
    I dont need exception i have an array of Tok[] objects i give it to Arrays.sort() and i get Null exception.
  • AlexWien
    AlexWien over 11 years
    Yes which Exception? A Nullpointer? This is good! You should not have a Null Object in your Tok[]. Make sure to not add null objectr to your Tok[]
  • Sterling Duchess
    Sterling Duchess over 11 years
    But thats a problem i know i can shorten my Tok[] not to contain any null indexes however this means i need to create another array count the number of non null indexes then copy this array in to a temporary array and sort that one. Its just to complex i simply want to put all the null elements at the bigest and put them at the end.
  • AlexWien
    AlexWien over 11 years
    No, no you could create a copy of that array without null elements, or you will later search your bug for 3 or more days, and end up saying java sort, TreeMap, hashMap has a bug. (Like working colleagues told once ;-))
  • AlexWien
    AlexWien over 11 years
    Emmerioch is right, trust us, do not Null check, see my answer below, but make your code clean, be removing or avoiding null objects in your Tok[] you want to sort! Go the clean way, Otherwise you spend much time searching a bug, you probaly will not find easily.
  • Matt Ball
    Matt Ball over 11 years
    @AlexWien you seem to be really stuck on the OP using some type of self-sorting collection, but this is clearly not the use case.
  • Matt Ball
    Matt Ball over 11 years
    Who says the OP is using a TreeSet or TreeMap?
  • AlexWien
    AlexWien over 11 years
    @matt The OP said in one of his comments , he uses sort() which uses compateTo() .
  • Matt Ball
    Matt Ball over 11 years
    @AlexWien ...which specifically implies the OP is not using a TreeSet or TreeMap.
  • EMMERICH
    EMMERICH over 11 years
    I think it's fine to check for null in compareTo as long as you throw the appropriate Exception. The obj may come from a data source out of your control, or you may be providing a utility for objects outside your control.
  • AlexWien
    AlexWien over 11 years
    @matt and emmerich: then the compareTo must be an own Comparator class, not part of Tok. And should be named "InsaneTokComparator"
  • AlexWien
    AlexWien over 11 years
    The point is that he uses compareTo in a way that violates the compareTo() contract of java.
  • Matt Ball
    Matt Ball over 11 years
    Chill out. Comparators are allowed to be null-safe; the JavaDoc explicitly says so: "Unlike Comparable, a comparator may optionally permit comparison of null arguments, while maintaining the requirements for an equivalence relation."
  • kbec
    kbec about 10 years
    @emmerich ...or input should be wrapped to take control of the nulls.