The Comparator interface

In the previous post about the Comparable interface I explained how you can extend a class of objects that implement the Comparable interface and then order them in a list according to the logic defined in the overridden method compareTo.
If you want to be able to sort the list according to a number of different criteria you must use the Comparator interface, for example, in the case of a list of countries, it is possible that you want to order them not only alphabetically but also in order of area or population.
A very important difference between Comparable and Comparator is as follows:

  • using Comparable, you must modify the object class you want to sort implementing Comparable
  • using Comparator, you must create a new class that implements Comparator; this new class can be a public class in its own file. java, or can also be an inner class because it is intimately tied to the outer class of objects to be sorted

The only method required by the Comparator interface is compare which has as argument 2 objects of the same type you are sorting and the return value is an integer.
So the call to the method is:
int result = compare(x, y)
where x and y are objects of the same type of those in the list and result is an integer that takes the value:

  • <0 if x<y
  • ==0 if x==y
  • >0 if x>y

After you have created the class Comparator you can use the static method Collections.sort(List list, Comparator c) to sort the list.
Look at the following example:

  • Main.java
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    
    public class Main {
    
    	public static void main(String[] args) {
    
    		List<Country> myCountryList = new ArrayList<Country>();
    
    		Country germany = new Country("Germany", 357021, 81305856);
    		Country french = new Country("French", 547030, 65630692);
    		Country unitedKingdom = new Country("United Kingdom", 244820, 63047162);
    		Country italy = new Country("Italy", 301230, 61261254);
    		Country spain = new Country("Spain", 504851, 47042984);
    
    		myCountryList.add(germany);
    		myCountryList.add(french);
    		myCountryList.add(unitedKingdom);
    		myCountryList.add(italy);
    		myCountryList.add(spain);
    
    		// FIRST SECTION
    
    		System.out.println("\nSORTED LIST BY NAME:");
    		Collections.sort(myCountryList);
    
    		for (Country item : myCountryList) {
    			System.out.println(item.getName());
    		}
    
    		// SECOND SECTION
    
    		System.out.println("\nSORTED LIST BY AREA:");
    		Collections.sort(myCountryList, new Country.AreaComparator());
    
    		for (Country item : myCountryList) {
    			System.out.println(item.getName());
    		}
    
    		// THIRD SECTION
    
    		System.out.println("\nSORTED LIST BY POPULATION IN DESCENDING ORDER:");
    		Collections.sort(myCountryList,
    				Collections.reverseOrder(new Country.PopulationComparator()));
    
    		for (Country item : myCountryList) {
    			System.out.println(item.getName());
    		}
    
    		// FOURTH SECTION
    
    		System.out.println("\nSORTED LIST BY NAME IN DESCENDING ORDER:");
    		Collections.sort(myCountryList, Collections.reverseOrder());
    
    		for (Country item : myCountryList) {
    			System.out.println(item.getName());
    		}
    
    	}
    }
    
  • Country.java
    import java.util.Comparator;
    
    public class Country implements Comparable<Country> {
    
    	static class AreaComparator implements Comparator<Country> {
    
    		@Override
    		public int compare(Country o1, Country o2) {
    			int result;
    			result = o1.getArea() - o2.getArea();
    			return result;
    		}
    	}
    
    	static class PopulationComparator implements Comparator<Country> {
    
    		@Override
    		public int compare(Country o1, Country o2) {
    			int result;
    			result = o1.getPopulation() - o2.getPopulation();
    			return result;
    		}
    	}
    
    	private int area;
    	private String name;
    	private int population;
    
    	public Country(String name, int area, int population) {
    		this.name = name;
    		this.area = area;
    		this.population = population;
    	}
    
    	@Override
    	public int compareTo(Country o) {
    		int result;
    		result = name.compareTo(o.getName());
    		return result;
    	}
    
    	public int getArea() {
    		return area;
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public int getPopulation() {
    		return population;
    	}
    
    	public void setArea(int area) {
    		this.area = area;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    
    	public void setPopulation(int population) {
    		this.population = population;
    	}
    
    }
    

The output is:

SORTED LIST BY NAME:
French
Germany
Italy
Spain
United Kingdom

SORTED LIST BY AREA:
United Kingdom
Italy
Germany
Spain
French

SORTED LIST BY POPULATION IN DESCENDING ORDER:
Germany
French
United Kingdom
Italy
Spain

SORTED LIST BY NAME IN DESCENDING ORDER:
United Kingdom
Spain
Italy
Germany
French

The class Country implements Comparable, this is not necessary to use Comparator but it is only useful to be able to make a comparison.
In the class Country there are 2 static inner classes (static to be used without an instance of the class Country) that override the compare method.
There are 4 sections in Main.java: in the first section the sort is by name without using Comparator but only the Comparable interface, in the second section the sort is by area; in the third section of the sort is descending by population (reverseOrder (Comparator cmp) returns a comparator that imposes the reverse ordering of the specified comparator); in the fourth section the sort is descending by name (reverseOrder () returns a comparator that imposes the reverse order of that defined in the Comparable interface).

Similarly, you can also sort the arrays using the Arrays.sort(Object[] a, Comparator c) method.


Comments

6 responses to “The Comparator interface”

  1. Salve.. complimenti per l’articolo mi è stato molto d’aiuto.. mi chiedevo se a posto delle liste avremmo usato dei Set ad esempio TreeSet, nel momento in cui ordiniamo con un Comparator esso eliminerà ad esempio due nazioni con la stessa popolazione o la stessa area, è possibile ovviare a questo problema? oppure è necessario affidarsi a Comparable implementando hashCode e equals riguardanti il solo nome della nazione? grazie

    1. Per evitare di aggiungere oggetti uguali è possibile usare l’interfaccia Set ma gli oggetti devono implementare il metodo equals che definisce cosa si intende per uguali.
      In tal caso oggetti uguali possono essere creati ma non aggiunti contemporaneamente al Set

  2. Ciao!
    Solo una domanda.

    Perchè il metodo “compare” delle varie classi AreaComparator e PopolutazionComparator viene chiamato in automatico alla creazione di un istanza delle due classi? Come mai non hai bisogno di fare un “.compare(var 1 var 2)” da nessuna parte?

    Grazie 1000 se mi togli un po di confusione!

    1. Il metodo compare non è usato chiamandolo direttamente, al metodo sort di Collections passi come argomento la lista da ordinare e una classe che implementa Comparator e quindi il metodo compare, sarà poi il metodo Collections.sort che chiamerà il metodo compare nel codice sorgente di java

  3. Cioè ti ringrazio non una ma diecimila volte…
    Seguendo corsi all’università,vari tutorial su internet,rileggendomi ventimilavolte le pagine del libro su quest’argomento non sono mai riuscita a capirlo…sono venuta qui ed ho seguito passo passo ciò che hai scritto implementando anche le classi….e finalmente ci sono riuscita…
    Te ne sono veramente grata…

  4. Ben fatto, grazie!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.