Java: null safe compareTo method
Solution 1
Personally, I like Guava's Ordering
for null-safe comparing. You can specify #nullsFirst()
or #nullsLast()
to avoid NullPointerException
s.
Other important notes, mostly from the comments:
-
this
is nevernull
in Java - Consider using Guava's
ComparisonChain
if you're implementing a fine-grainedcompareTo()
-
When implementing
Comparable
, be sure to specify the type parameter so you get compile-time type safety and don't have to useinstanceof
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
Sterling Duchess
Updated on June 30, 2020Comments
-
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
, ifnull
simply return0
; what would be the easiest way to implement null-safecompareTo
.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 over 11 yearsSpecifically a NullPointerException according to Comparable's documentation. docs.oracle.com/javase/6/docs/api/java/lang/Comparable.html
-
Sterling Duchess over 11 yearsputting array of Tok objects in to Arrays.sort(tok[]) still yields an exception.
-
Sterling Duchess over 11 yearsI dont need exception i have an array of Tok[] objects i give it to Arrays.sort() and i get Null exception.
-
AlexWien over 11 yearsYes 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 over 11 yearsBut 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 over 11 yearsNo, 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 over 11 yearsEmmerioch 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 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 over 11 yearsWho says the OP is using a
TreeSet
orTreeMap
? -
AlexWien over 11 years@matt The OP said in one of his comments , he uses sort() which uses compateTo() .
-
Matt Ball over 11 years@AlexWien ...which specifically implies the OP is not using a
TreeSet
orTreeMap
. -
EMMERICH over 11 yearsI 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 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 over 11 yearsThe point is that he uses compareTo in a way that violates the compareTo() contract of java.
-
Matt Ball over 11 yearsChill out.
Comparator
s are allowed to be null-safe; the JavaDoc explicitly says so: "UnlikeComparable
, a comparator may optionally permit comparison of null arguments, while maintaining the requirements for an equivalence relation." -
kbec about 10 years@emmerich ...or input should be wrapped to take control of the nulls.