Question

In Java, I use a class in which some fields can be null. For example:

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

I want to write a BarComparator for this class,

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

Is there a standard way to deal with the fact that any of o1, o2, o1.bar, o2.bar can be null, without writing lots of nested if...else?

Cheers!

Was it helpful?

Solution

I guess you could wrap the call to the field compareTo method with a small static method to sort nulls high or low:

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

Simple usage (multiple fields is as you would normally):

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

OTHER TIPS

Thanks for the replies! The generic method and the Google Comparators look interesting.

And I found that there's a NullComparator in the Apache Commons Collections (which we're currently using):

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

Note: I focused on the bar field here to keep it to the point.

It depends on whether you consider a null entry to be a valid string value worth of comparison. is null < or > "apple". The only thing I could say for sure is that null == null. If you can define where null fits into the ordering then you can write the code appropriately.

In this case I might choose to throw a NullPointerExcpetion or IllegalArgumentException and try to handle the null at a higher level by not putting it in the comparison in the first place.

You can write your Comparator for it. Lets say you have a class Person with String name as private field. getName() and setName() method to access the field name. Below is the Comparator for class 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());
        }
    });

Update:

As of Java 8 you can use below API's for List.

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

The key thing here is to work out how you would like nulls to be treated. Some options are: a) assume nulls come before all other objects in sort order b) assume nulls come after all other objects in sort order c) treat null as equivalent to some default value d) treat nulls as error conditions. Which one you choose will depend entirely on the application you are working on.

In the last case of course you throw an exception. For the others you need a four-way if/else case (about three minutes of coding one you've worked out what you want the results to be).

If you're using Google collections, you may find the Comparators class helpful. If has helper methods for ordering nulls as either the greatest or least elements in the collection. You can use compound comparators to help reduce the amount of code.

There is also the class org.springframework.util.comparator.NullSafeComparator in the Spring Framework you can use.

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

This will print:

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

It seems to me there is not a method to do it, but anyway the code is not so long.

You should not use the NullComparator the way you do - you're creating a new instance of the class for every comparison operation, and if e.g. you're sorting a list with 1000 entries, that will be 1000 * log2(1000) objects that are completely superfluous. This can quickly get problematic.

Either subclass it, or delegate to it, or simply implement your own null check - it's really not that complex:

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

Considering Customer as a POJO.My answer would be :

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

Or

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

I think early return statements would be the other alternative to lots of ifs

e.g.

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.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top