Tuesday, 20 January 2015

Comparable and Comparator

By default a collection is sorted in natural order: numbers and Strings are sorted in ascendant order. Note that for Strings upper-cases comes before lower-cases and spaces comes before any characters.You can modify the default behaviour of 'natural order' by implementing a 'Comparable' or/and 'Comparator' interfaces for your classes.

The interface 'Comparable'


'Comparable' interface is used by the static methods "Collections.sort()" and "Arrays.sort()". If you have a collection which holds a custom class' objects (like 'Person' objects) and if you want to sort this collection with using "Collections.sort()" or "Arrays.sort()" methods, make sure that the custom class implements 'Comparable'. Otherwise you will get a compilation error.

Definition of the class 'Person':
    class Person implements Comparable<Person> {
        public String firstName;

        Person(String firstName) {
            this.firstName = firstName;
        }

        // By default we want to sort a collection of persons by firstName
        public int compareTo(Person person){
            return this.firstName.compareTo(person.firstName);
        }
    }

Example of usage with "Collections.sort()":
    // We create a collection of persons
    List<Person> persons = new ArrayList<Person>();
    persons.add(new Person("Jean"));
    persons.add(new Person("Tom"));
    persons.add(new Person("Joe"));
    persons.add(new Person("Alice"));

    Collections.sort(persons);

    // We iterate through the collection of persons
    for(Person person : persons){
        System.out.println("- "+person.firstName);
    }
Output:
- Alice
- Jean
- Joe
- Tom

Note that the method 'compareTo()' returns:
  • 0 (zero) : if the compared objects are equal
  • 1 (zero) : if thisObject is > to the anotherObject
  • -1 (zero) :if thisObject is < to the anotherObject

The interface 'Comparator'


As we have seen, the interface 'Comparable' allows to define a unique 'sorting' option for a class. If you want to add more than one 'sorting' options to your class then the interface 'Comparator' is the perfect candidate. Let's complete the previous example with adding to the class 'Person', an attribute 'Integer age' that sort in ascending order.
    class Person implements Comparable<Person> {
        public String firstName;
        public Integer age;
       
        Person(String firstName, Integer age){
            this.firstName = firstName;
            this.age = age;
        }
       
        // By default we want to sort a collection of persons by firstName
        public int compareTo(Person person){
            return this.firstName.compareTo(person.firstName);
        }
       
        // Optionally we want to sort a collection of persons by age
       static class AgeComparator implements Comparator<Person> {
           
            public int compare(Person person1, Person person2){
                return person1.age.compareTo(person2.age);
            }
        }
    }

Example of usage with "Collections.sort()":
    // We create a collection of persons
    List<Person> persons = new ArrayList<Person>();
    persons.add(new Person("Jean", 45));       // Note that autoboxing in the second argument
    persons.add(new Person("Tom", 13));
    persons.add(new Person("Joe", 27));
    persons.add(new Person("Alice", 11));
   
    // We pass in the second argument of the method 'sort()' an instance of our custom comparator class
    Collections.sort(persons, new Person.AgeComparator());
   
    // We iterate through the collection of persons
    for(Person person : persons){
       System.out.println("- "+person.firstName+" is "+person.age+" years old");
    }
Output:
- Alice is 11 years old
- Tom is 13 years old
- Joe is 27 years old
- Jean is 45 years old