Java Stream – Sort with Null Values

Learn to sort a Java List or Stream with Comparator‘s nullsFirst() and nullsLast() methods. The Stream may contain null values, or the custom objects may have null field values.

Failing to handle the null values during comparison will cause NullPointerException in runtime.

1. Introduction

In this tutorial, we will learn the usage of the following methods:

  • Comparator.nullsFirst() – Returns a null-friendly comparator if at least one value (under comparison) is null. It considers null to be less than non-null. If both side of comparison are null then both are considered equal.
  • Comparator.nullsLast() – – Returns a null-friendly comparator if at least one value (under comparison) is null. It considers null to be greater than non-null. If both side of comparison are null then both are considered equal.

Infact, above both methods returns an instance of NullComparator that adds necessary null checks while comparing both sides for sorting purposes.

Here, nullFirst refer to the null-first or null-last approach. And real refers to original Comparator is both sides are non-null values.

static final class NullComparator<T> 
	implements Comparator<T>, Serializable {
	...
	...
	public int compare(T a, T b) {
	    if (a == null) {
	        return (b == null) ? 0 : (nullFirst ? -1 : 1);
	    } else if (b == null) {
	        return nullFirst ? 1: -1;
	    } else {
	        return (real == null) ? 0 : real.compare(a, b);
	    }
	}
	...
	...
}

2. Sorting Stream of Custom Objects

Let us assume we are sorting a list of Employee objects. We want to sort a given stream of employees by their date of birth.

public class Employee 
{
    private long id;
    private String name;
    private LocalDate dateOfBirth;
    private double salary;

    //constructors, setters and getters are hidden for brevity

    @Override
    public String toString() {
        return "Employee [id=" + id + ", name=" + name + ", dateOfBirth="
            + dateOfBirth + ", salary=" + salary + "]";
    }
}

2.1. Few Custom Objects in List are Null

Now suppose, there are a few employee objects which are null. In the given example, we have added two nulls in the Stream along with 3 employees.

We are sorting using the nullsFirst() method, after sorting, null values will come in the start and then the sorting list of employees will come sorted by date of birth.

To sort on natural order, after ordering null values, use Comparator.nullsFirst( Comparator.naturalOrder() ).

public class DateOfBirthComparator implements Comparator<Employee> {

    @Override
    public int compare(final Employee e1, final Employee e2) {
        return e1.getDateOfBirth().compareTo(e2.getDateOfBirth());
    }
}
List<Employee> sortedEmployeeList = getEmployeeListWithNullObjects()
    .stream()
    .sorted(Comparator.nullsFirst(new DateOfBirthComparator()))
    .collect(Collectors.toList());

System.out.println(sortedEmployeeList);

...
...

private static List<Employee> getEmployeeListWithNullObjects() {
    List<Employee> empList = new ArrayList<>();
    empList.add(new Employee(1, "A", LocalDate.of(1992, 1, 1), 30000d));
    empList.add(null);
    empList.add(new Employee(3, "C", LocalDate.of(1992, 9, 1), 50000d));
    empList.add(null);
    empList.add(new Employee(5, "E", LocalDate.of(1992, 8, 1), 60000d));
    return empList;
}

Program Output.

[null, 
null, 
Employee [id=1, name=A, dateOfBirth=1992-01-01, salary=30000.0], 
Employee [id=5, name=E, dateOfBirth=1992-08-01, salary=60000.0], 
Employee [id=3, name=C, dateOfBirth=1992-09-01, salary=50000.0]]

2.2. Custom Object’s Field Values are Null

In some scenarios, we may have a stream of objects where the objects are non-null, but their field values may be null. For example, in stream of employees, all employee objects may be non-null but few employees may not have date of birth information.

In such cases, we have two options to sort the employee instances:

2.2.1. Implement Extra Null-checks in Custom Comparator

To avoid NullPointerException, we can write the null-checks ourselves in the custom Comparator class.

public class DateOfBirhComparator implements Comparator<Employee> {

    @Override
    public int compare(final Employee e1, final Employee e2) {
        if (e1.getDateOfBirth() == null && e2.getDateOfBirth() == null) {
            return 0;
        } else if(e1.getDateOfBirth() == null) {
            return -1;
        } else if(e2.getDateOfBirth() == null) {
            return 1;
        } else {
            return e1.getDateOfBirth().compareTo(e2.getDateOfBirth());
        }
    }
}

And then we use this comparator for sorting purposes. Notice the output that the first two records have date of birth field as null.

sortedEmployeeList = getEmployeeListWithNullDates().stream()
    .sorted(new DateOfBirhComparator())
    .collect(Collectors.toList());

private static List<Employee> getEmployeeListWithNullDates() {
    List<Employee> empList = new ArrayList<>();
    empList.add(new Employee(1, "A", LocalDate.of(1991, 1, 1), 30000d));
    empList.add(new Employee(2, "B", null, 20000d));
    empList.add(new Employee(3, "C", LocalDate.of(1992, 8, 1), 50000d));
    empList.add(new Employee(4, "D", LocalDate.of(2001, 3, 11), 50000d));
    empList.add(new Employee(5, "E", null, 60000d));
    return empList;
}
[
Employee [id=2, name=B, dateOfBirth=null, salary=20000.0], 
Employee [id=5, name=E, dateOfBirth=null, salary=60000.0], 
Employee [id=1, name=A, dateOfBirth=1991-01-01, salary=30000.0], 
Employee [id=3, name=C, dateOfBirth=1992-08-01, salary=50000.0], 
Employee [id=4, name=D, dateOfBirth=2001-03-11, salary=50000.0]
]

Note that if we want to handle both scenarios where either the Employee object can be null or the dateOfBirth field can be null then we can use the combination of DateOfBirhComparator and NullComparator.

sortedEmployeeList = getEmployeeListWithNullDates().stream()
    .sorted(Comparator.nullsFirst(new DateOfBirhComparator()))
    .collect(Collectors.toList());

2.2.2. Using Comparator.comparing()

We can provide the inline custom comparator with Lambda syntax in the following manner.

sortedEmployeeList = getEmployeeListWithNullDates().stream()
    .sorted(Comparator.comparing(Employee::getDateOfBirth,
        	Comparator.nullsFirst(Comparator.naturalOrder())))
    .collect(Collectors.toList());

The above code will also provide the same functionality as the custom Comparator.

3. Sorting with Nulls in Natural Order

For inbuilt Java types such as primitives, wrapper classes and Strings, we can use their natural ordering for the sorting purpose. The only thing we need to take care of is that any null value should not break the code with NullPointerException.

3.1. Ordering the Nulls in Last

Use Comparator.nullsLast(Comparator.naturalOrder()) for ordering non-null values in begin and null values in the last.

List<String> names = Arrays.asList("C", null, "B", "D", null, "A", "E");

List<String> sortedList = names.stream()
    .sorted(Comparator.nullsLast(Comparator.naturalOrder()))
    .collect(Collectors.toList());

System.out.println(sortedList);  //[A, B, C, D, E, null, null]

3.2. Ordering the Nulls in Beginning

Use Comparator.nullsFirst(Comparator.naturalOrder()) for ordering null values in begin and non-null values in the last.

List<String> names = Arrays.asList("C", null, "B", "D", null, "A", "E");

List<String> sortedList = names.stream()
    .sorted(Comparator.nullsFirst(Comparator.naturalOrder()))
    .collect(Collectors.toList());

System.out.println(sortedList);    //[null, null, A, B, C, D, E]

4. Conclusion

In this Java Stream tutorial, we learned to sort a stream of strings and a stream of custom objects. The sorting logic will be able to handle the cases when the stream may have null values or the objects in the stream may have null fields.

We learned how NullComparator provides internal null-checks which we can add using a custom Comparator class as well.

Happy Learning !!

Download Sourcecode

Was this post helpful?

Join 7000+ Awesome Developers

Get the latest updates from industry, awesome resources, blog updates and much more.

* We do not spam !!

Leave a Comment

HowToDoInJava

A blog about Java and related technologies, the best practices, algorithms, and interview questions.