Comparator naturalOrder, reverseOrder and reversed

Introduction

I have written a post on the Comparator comparing methods earlier. As a follow up of that, in this post we will learn about the Comparator naturalOrderreverseOrder and reversed methods.

Example setup

For this post, let us use a simple Order class shown below. It has the order id, the total, the customer id for which the order belongs to and the list of items in the order.

public class Order implements Comparable<Order> {
    private int id;
    private double total;
    private int customerId;
    private List<String> items;

    public Order(int id, double total, int customerId, List<String> items) {
        this.id = id;
        this.total = total;
        this.customerId = customerId;
        this.items = new ArrayList<>(items);
    }

    public int getId() {
        return id;
    }

    public double getTotal() {
        return total;
    }

    public int getCustomerId() {
        return customerId;
    }

    public List<String> getItems() {
        return Collections.unmodifiableList(items);
    }

    @Override
    public int compareTo(Order o) {
        return Integer.compare(this.id, o.id);
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("OrderId: ")
                .append(id)
                .append(", ");
        builder.append("Items: [");
        
        //or builder.append(String.join("", items));
        builder.append(items.stream()
                .collect(Collectors.joining(", ")));
                
        builder.append("]");
        return builder.toString();
    }
}

In the constructor, we are copying the passed list of items before assigning to the instance variable to keep the class immutable. Also, we are returning an unmodifiable list from the getItems() method. Check out how to make a class immutable and benefits of immutable class posts to learn more.

In the toString() method, we are building a string representation containing the order id and the list of items. We are using Collectors#joining to build the items list joined by comma.

The Order class implements the Comparable interface and the compareTo method uses the order id to form the natural ordering. Thus if we have two Order instances, the one with the lower order id would be smaller than the one with the higher order id (in their natural ordering).

Comparator naturalOrder

The Comparator naturalOrder has the following signature:

public static <T extends Comparable<? super T>> Comparator<T> naturalOrder() {...}

It returns a comparator that compares objects in natural order. Thus, this requires the objects we compare to implement the Comparable interface. In this example, the Order class implements the Comparable interface.

List<Order> orders = List.of(
        new Order(2, 10.00, 1, List.of("item-1", "item-2")),
        new Order(3, 20.00, 2, List.of("item-1", "item-3")),
        new Order(1, 15.50, 3, List.of("item-4"))
);

System.out.println(orders);
List<Order> sortedOrders = new ArrayList<>(orders);
sortedOrders.sort(Comparator.naturalOrder());
System.out.println(sortedOrders);

In the above code, we create a list of Order objects using the List.of method added in Java 9 (Use Arrays.asList if you are using Java 8). Then we sort the list by calling Comparator.naturalOrder static method. Since it will sort the passed list, the list must be mutable – hence we are copying the orders list before calling the naturalOrder method. The result is:

[OrderId: 2, Items: [item-1, item-2], OrderId: 3, Items: [item-1, item-3], OrderId: 1, Items: [item-4]]
[OrderId: 1, Items: [item-4], OrderId: 2, Items: [item-1, item-2], OrderId: 3, Items: [item-1, item-3]]

Printing the original orders list prints the objects in the order 2, 3 and 1. After sorting them using their natural order, we get the objects in the below order:

  • order 1 having item 4 as the first order object in the natural ordering
  • order 2 having items 1 and 3
  • finally, order 3
since the compareTo implementation of the Order class uses order id to determine the natural ordering.

Comparator reverseOrder

The Comparator reverseOrder method signature is:

public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() {...}

It returns a comparator that imposes the reverse of the natural order. Using it in our orders example,

List<Order> orders = List.of(
        new Order(2, 10.00, 1, List.of("item-1", "item-2")),
        new Order(3, 20.00, 2, List.of("item-1", "item-3")),
        new Order(1, 15.50, 3, List.of("item-4"))
);

List<Order> sortedOrders = new ArrayList<>(orders);
sortedOrders.sort(Comparator.reverseOrder());

Prints,

[OrderId: 3, Items: [item-1, item-3], OrderId: 2, Items: [item-1, item-2], OrderId: 1, Items: [item-4]]

Since we are using the reverse of the natural order, we get the order objects in the order – 3, 2 and then 1.

Comparator reversed

The final method we will see is the reversed method. Unlike the naturalOrder and reverseOrder methods, this is not a static method. Thus, we can call it only on a Comparator instance.

Similar to the reverseOrder() method, it returns a comparator that imposes a reverse ordering. But rather than taking a comparator as a parameter, it returns a reversed comparator of the comparator instance on which this method is called.

Let us use the same Order list and we have a comparator that orders by the total of the Order instance (rather than by the order id).

List<Order> orders = List.of( //see
        new Order(2, 10.00, 1, List.of("item-1", "item-2")),
        new Order(3, 20.00, 2, List.of("item-1", "item-3")),
        new Order(1, 15.50, 3, List.of("item-4"))
);

Comparator<Order> sortByTotal = Comparator.comparingDouble(Order::getTotal);

List<Order> sortedOrders = new ArrayList<>(orders);
sortedOrders.sort(sortByTotal);
System.out.println(sortedOrders);

Prints,

[OrderId: 2, Items: [item-1, item-2], OrderId: 1, Items: [item-4], OrderId: 3, Items: [item-1, item-3]]

Using sortByTotal comparator to sort the list of orders, we get the order with the lowesttotal as the first item of the list. In other words, using the sortByTotal comparator, we have sorted the orders list from lowest to highest in terms of the order amount (total). 

  • Order id 2 has $10.00 as total
  • Order id 1 has $15.5
  • And the order id 3 has $20.0

If we want to sort the other way around (the highest total to lowest), we can call the reversed() method on the sortByTotal comparator as shown below.

List<Order> sortedOrders = new ArrayList<>(orders);
sortedOrders.sort(sortByTotal.reversed());
System.out.println(sortedOrders);

Prints,

[OrderId: 3, Items: [item-1, item-3], OrderId: 1, Items: [item-4], OrderId: 2, Items: [item-1, item-2]]

Note this is the reverse of the previous sorted orders output (i.e., when using sortByTotal).

Conclusion

In this post, we have learnt about the Comparator methods naturalOrderreverseOrder and reversed. I recommended checking out the other posts on Comparator.

Leave a Reply