Choosing Between Comparable and Comparator Interfaces in Java: When to Use What for Object Sorting
Tags: java, beginners
Published: 2023-07-29 09:16:08

When dealing with collections of objects in Java, sorting or comparing objects based on specific criteria is a common requirement. To address this, Java offers two essential interfaces: Comparable and Comparator.

The Comparable Interface:

The Comparable interface provides a means of establishing a natural ordering for objects of a class. It allows you to define a default way of sorting objects, which is considered the “natural order” for that class. By implementing Comparable, you are essentially declaring that instances of your class can be compared to one another and sorted accordingly.

Key points about the Comparable interface:

Total Ordering: The Comparable interface imposes a total ordering on the objects of the class that implements it. This means that any two objects can be compared and will always be in a well-defined order concerning each other.
Functional Interface: Comparable is a functional interface, which means it has a single abstract method called compareTo(Object obj). This method is where you define the logic to compare two instances of your class.
Part of java.lang Package: The Comparable interface is part of the java.lang package, so it is automatically available for all Java classes.
When to use the Comparable Interface:

Use the Comparable interface when you want to define a default or natural ordering for your class, and when you don’t need multiple ways of sorting objects. By implementing Comparable, you enable straightforward sorting using built-in methods like Collections.sort() and Arrays.sort().

```
public static void main(String[] args) {
        // Integer and String class implement Comparable interface
        List<Integer> integerList = Arrays.asList(2, 4, 5, 1);
        List<String> stringList = Arrays.asList("b", "d", "a", "c");

        Collections.sort(integerList); // o/p --> 1,2,4,5
        Collections.sort(stringList); // o/p --> a,b,c,d

        int [] array = new int[]{2,1,5,3};
        Arrays.sort(array);
        System.out.println(Arrays.toString(array)); // 1,2,3,5
    }
```

```
// Natural Sorting of Custom Objects 
public class Person implements Comparable<Person> {
    private String name;
    private int age;

    // Constructors, getters, and setters // toString()

    @Override // comparable method sorting based on name or age 
    public int compareTo(Person person) {
    
      //  return Integer.compare(this.age, person.age);
        return this.name.compareTo(person.name);
    }
// Sorting the list of Person objects
List<Person> personList = new ArrayList<>();
personList.add(new Person("Alice", 25));
personList.add(new Person("Bob", 30));
personList.add(new Person("Charlie", 20));
Collections.sort(personList); // {name='Alice', age=25}, {name='Bob', age=30}, {name='Charlie', age=20}
System.out.println(personList);
}
```

The Comparator Interface:

The Comparator interface provides a more flexible approach to sorting. It allows you to define multiple custom sorting criteria for a class without modifying the class itself. With Comparator, you can create different sorting strategies for the same class, depending on different requirements or use cases.

Key points about the Comparator interface:

Custom Sorting Criteria: Comparator enables you to implement various sorting rules for a class that might be different from its natural ordering. This is useful when you want to sort objects based on different attributes or conditions.
Functional Interface: Like Comparable, Comparator is also a functional interface with a single abstract method called compare(T obj1, T obj2). This method defines the logic for comparing two instances of the class.
External Sorting: Comparator allows you to sort classes that you don’t have control over or classes from third-party libraries. It provides a way to add sorting behaviour to those classes externally.
When to use the Comparator Interface:

Use the Comparator interface when you need to implement custom sorting orders for a class or when you want to sort objects based on different attributes or conditions at runtime. Comparator offers more flexibility and is especially valuable when working with classes you cannot modify or classes with multiple valid sorting criteria.

```
public class Review {
    private double rating;
    private String comment;

  
// getters setters tostring()

    public Review(final double rating, final String comment) {
        this.rating = rating;
        this.comment = comment;
    }

    public static void main(String[] args) {
        Review review1 = new Review(4.2,"Good content");
        Review review2 = new Review(4.9,"Awesome content");
        Review review3 = new Review(3.1,"Average content");
        Review review4 = new Review(2.9,"Bad content");

        List<Review> reviewList = Arrays.asList(review1, review2, review3, review4);

        // sorting the list based on rating

 
        Collections.sort(reviewList,new RatingComparator());
        System.out.println(reviewList);

  //In java 8 using lambdas
  // sort method takes in a comparator 
Collections.sort(reviewList,(r1, r2)-> Double.compare(r1.getRating(), r2.getRating()));
// or 
reviewList.sort((r1, r2) -> Double.compare(r1.getRating(), r2.getRating()));
// or using Comparator.comparingDouble method
reviewList.sort(Comparator.comparingDouble(Review::getRating));
        
    }

}
class RatingComparator implements Comparator<Review>
{

    @Override
    public int compare(final Review review1, final Review review2) {
        return Double.compare(review2.getRating(),review1.getRating());
    }
}
Custom Sorting based on Map Values instead of keys
 public class SortingBasedOnMapValues {
    public static void main(String[] args) {

        HashMap<String, Integer> map = new HashMap<>();
        map.put("ron", 21);
        map.put("sheena", 18);
        map.put("ajay", 23);
        map.put("vijay", 25);
        map.put("bob", 12);

        // default sorting based on keys ie names
        //  String class implements the Comparable Interface hence it is best option fo maps to use it as keys
        // also they are immutable
        TreeMap<String, Integer> keySortMap = new TreeMap<>(map);
        System.out.println(keySortMap);// {ajay=23, bob=12, ron=21, sheena=18, vijay=25}

        //the treemap takes in a comparator hence we can use our own default sorting ie we can use Comparator interface  compare method to sort values based on our need
        // sorting age in decreasing manner

        //  Java 8
        TreeMap<String, Integer> valueSortMap = new TreeMap<>((v1, v2) -> map.get(v2).compareTo(map.get(v1)));

        valueSortMap.putAll(map);
        System.out.println(valueSortMap);// {vijay=25, ajay=23, ron=21, sheena=18, bob=12}

        // sorting age in increasing manner
        // TreeMap<String, Integer> valueSortMap2 = new TreeMap<>((v1, v2) -> map.get(v1).compareTo(map.get(v2)));
        TreeMap<String, Integer> valueSortMap2 = new TreeMap<>(Comparator.comparing(map::get));
        valueSortMap2.putAll(map);
        System.out.println(valueSortMap2);// {bob=12, sheena=18, ron=21, ajay=23, vijay=25}

        // Before Java 8
        class AgeComparator implements Comparator {

            @Override
            public int compare(final Object o1, final Object o2) {
                return map.get(o2).compareTo(map.get(o1));
            }
        }
        TreeMap<String, Integer> integerTreeMap = new TreeMap<>(new AgeComparator());
        integerTreeMap.putAll(map);
        System.out.println(integerTreeMap);
    }
}
```

```
Comparison done based on a fixedList
public class FixedListSortingExample {

// this is a fixed list
    private static final List<String> fixedList = Arrays.asList("Book your Tickets", "Pack your Bag",
            "Go to the Airport", "Check in", "Show Your Id", "Get Inside", "Sit",
            "Final Destination", "Enjoy Your Trip");

    private static final Map<String, Integer> indexMap = new HashMap<>();

 // Create a map that assigns each value in the fixed list based on index
     static {
        for (int i = 0; i < fixedList.size(); i++) {
            indexMap.put(fixedList.get(i).toLowerCase(), i);
        }
    }

    public static void main(String[] args) {
        
     final String subject  ="Show Your Id,Pack your Bag,Book your Tickets";

         // sorting should be based on this custom list
        String sortBasedOnFixedList = sortBasedOnFixedList(subject);
        System.out.println(sortBasedOnFixedList);
    }

    public static String sortBasedOnFixedList(String subject) {
        final String[] input = subject.split(",");
// Sort the input values based on their order in the fixed list using a custom comparator
        sort(input, comparingInt(value -> indexMap.getOrDefault(value.toLowerCase(), Integer.MAX_VALUE)));
        return String.join(",", input);
    }
}

input is {"Show Your Id", "Pack your Bag", "Book your Tickets"};
o/p --
Book your Tickets
Pack your Bag
Show Your Id
```

```
Sort Days of a week 
public class FixedSortingEnums {
    static List<String> weekdays = Arrays.asList("Sunday", "Monday", "Saturday", "Thursday", "Wednesday", "Tuesday", "Friday");
    public static void main(String[] args) {

        // this will sort based on natural sorting order  since String implements the comparable interface
        // we need to sort in actual week format i.e Sunday,monday,tues,wed,thur,fri,sat
        Collections.sort(weekdays);
        System.out.println(weekdays); // [Friday, Monday, Saturday, Sunday, Thursday, Tuesday, Wednesday]

        // we use hashmap and index the value
        HashMap<String, Integer> week = new HashMap<>();
        week.put("Sunday", 0);
        week.put("Monday", 1);
        week.put("Tuesday", 2);
        week.put("Wednesday", 3);
        week.put("Thursday", 4);
        week.put("Friday", 5);
        week.put("Saturday", 6);

        

        // Use comparator for default sorting 
//        Comparator<String> weekComparator = new Comparator<String>() {
//            @Override
//            public int compare(final String s1, final String s2) {
//                Integer day1 = week.get(s1);
//                Integer day2 = week.get(s2);
//                return day1.compareTo(day2);
//            }
//        };
//        
//        Collections.sort(weekdays, weekComparator);
//        System.out.println(weekdays);
        
             //using lamda function
       //Collections.sort(weekdays,(s1,s2)->week.get(s1).compareTo(week.get(s2)));
       // or using Comparator function
        Collections.sort(weekdays, Comparator.comparing(week::get));// [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
        System.out.println(weekdays);

    }
```
Conclusion
In summary, use the Comparable interface when you want to provide a single natural ordering for a class, while the Comparator interface should be used when you need to define custom sorting behavior, multiple sorting orders, or when you cannot modify the class directly. By understanding the differences between these two interfaces, you can choose the appropriate approach to achieve efficient and flexible sorting in your Java applications.