Introduction

In this blog post, we will see three ways to sort a list in Java. We will see how to sort in ascending and in reversed order. We will see on sorting a list of objects whose class implements Comparable and for one that doesn’t.

When a class implements Comparable, it gives the objects of that class a natural way to compare and hence we can use to sort a list of such objects. When a class doesn’t implement Comparable, an explicit Comparator has to be passed when sorting. Moreover, an explicit Comparator can be used even when the object has Comparator implemented - say when we want to order the objects in an order other than what the Comparator orders by.

Sorting list of objects implementing Comparable

We have a Course class that has the name of the course and the semester. For simplicity, the toString just returns the name. The below code shows the Course class and creates a list of course object, courses, which we will sort.

public class Course implements Comparable<Course> {
    private String name;
    private Integer semester;

    Course(String name, Integer semester) {
        this.name = name;
        this.semester = semester;
    }

    @Override
    public int compareTo(Course o) {
        return name.compareTo(o.name);
    }

    public String getName() {
        return name;
    }

    public Integer getSemester() {
        return semester;
    }

    @Override
    public String toString() {
        return name;
    }
}
List<Course> courses = new ArrayList<>();
courses.add(new Course("Discrete Mathematics", 4));
courses.add(new Course("Basic Physics", 1));
courses.add(new Course("Data structures", 3));
courses.add(new Course("Object Oriented Programming", 2));

The compareTo method orders by the name of the course alone. Since, it is a string, the courses will be sorted/ordered lexicographically.

Using Collections sort utility

In the first approach, we use the sort utility method on the Collections class. If we look at the implementation, this actually delegates to the sort method on the passed list. In other words, this is effectively the same as the next option. Anyway, if we are doing this, this would look like.

Collections.sort(courses);
System.out.println(courses);
//[Discrete Mathematics, Basic Physics, Data structures, Object Oriented Programming]

Using sort method on the List

The sort method on the List class mandates passing a Comparator. Since, the Courses implement the Comparable interface, we can pass a null as the Comparator.

courses.sort(null);
System.out.println(courses);
//[Discrete Mathematics, Basic Physics, Data structures, Object Oriented Programming]

Sorting when streaming

We can stream the elements of the list and call the sorted method on the stream to sort it. When compared to the other two methods, this should suffer from a performance penalty unless the list size is very big.

Also, unlike the other two methods, this does not leave the original list sorted. It creates a new list whose elements are sorted. If we want to achieve the same in the first two methods, we should copy the list before sorting.

List<Course> sortedCourses = courses.stream()
        .sorted()
        .collect(Collectors.toList());
System.out.println(sortedCourses);
//[Discrete Mathematics, Basic Physics, Data structures, Object Oriented Programming]

Reverse sorting list of objects implementing Comparable

Now, we will start with the same courses object and will sort it in reverse of the natural order. The trick is to use the Comparator.reverseOrder.

Using Collections sort utility

Pass Comparator.reverseOrder as the comparator to the sort method along with the list to be sorted.

Collections.sort(courses, Comparator.reverseOrder()); 
System.out.println(courses);
//[Object Oriented Programming, Discrete Mathematics, Data structures, Basic Physics]

Or, we can sort and reverse the list as

Collections.sort(courses);
Collections.reverse(courses);

Using sort method on the List

Pass Comparator.reverseOrder as the comparator to the sort method on the list.

courses.sort(Comparator.reverseOrder());
System.out.println(courses);
//[Object Oriented Programming, Discrete Mathematics, Data structures, Basic Physics]

Sorting when streaming

List<Course> reverseSortedCourses = courses.stream()
        .sorted(Comparator.reverseOrder())
        .collect(Collectors.toList());
System.out.println(reverseSortedCourses);
//[Object Oriented Programming, Discrete Mathematics, Data structures, Basic Physics]

Sorting list of object that does not implement Comparable

When the object in the list cannot be naturally ordered, the methods used earlier will fail with a java.lang.ClassCastException when it attempts to convert the object to a Comparable. The Collections.sort call would not even compile as the type parameter T of List<T> must implement Comparable. In this case, we have to pass a comparator to compare and impose a total ordering.

We will use the below Car class for this.

public static class Car {
    private String model;
    private String yearOfRelease;

    Car(String model, String yearOfRelease) {
        this.model = model;
        this.yearOfRelease = yearOfRelease;
    }

    public String getModel() {
        return model;
    }

    public String getYearOfRelease() {
        return yearOfRelease;
    }

    @Override
    public String toString() {
        return model;
    }
}
List<Car> cars = new ArrayList<>();
cars.add(new Car("Honda Civic", "2015"));
cars.add(new Car("Nissan NV", "2014"));
cars.add(new Car("Ford Mustang", "2019"));

Using Collections sort utility

We cannot use this as the type of the objects must implement Comparable.

Using sort method on the List

Pass a comparator that orders the cars by the model name.

cars.sort(Comparator.comparing(Car::getModel));
System.out.println(cars);
//[Ford Mustang, Honda Civic, Nissan NV]

The above Comparator is equivalent to the below lambda expression.

(car1, car2) -> car1.getModel().compareTo(car2.getModel())

To learn more on the Comparator comparing methods on Java 8, refer to the post on Comparator comparing.

Sorting when streaming

Pass the same comparator to the sorted method.

List<Car> sortedCars = cars.stream()
        .sorted(Comparator.comparing(Car::getModel))
        .collect(Collectors.toList());
System.out.println(sortedCars);
//[Ford Mustang, Honda Civic, Nissan NV]

Reverse sorting list of object that does not implement Comparable

Using Collections sort utility

We cannot use this as the type of the objects must implement Comparable as earlier.

Using sort method on the List

We can obtain a reverse Comparator by calling the reversed method on it.

cars.sort(Comparator.comparing(Car::getModel)
        .reversed());
System.out.println(cars);
//[Nissan NV, Honda Civic, Ford Mustang]

Sorting when streaming

List<Car> reverseSortedCars = cars.stream()
        .sorted(Comparator.comparing(Car::getModel).reversed())
        .collect(Collectors.toList());
System.out.println(reverseSortedCars);
//[Nissan NV, Honda Civic, Ford Mustang]

Conclusion

In this post, we learnt how to sort (and reverse sort) a list of objects when

a) the objects implement Comparable

b) the objects do not implement Comparable

References

https://howtodoinjava.com/sort/collections-sort/