Guide de Java 8 Comparator.comparing ()

1. Vue d'ensemble

Java 8 a introduit plusieurs améliorations à l' interface du comparateur , y compris une poignée de fonctions statiques qui sont d'une grande utilité lors de la création d'un ordre de tri pour les collections.

Les lambdas Java 8 peuvent également être exploités efficacement avec l' interface Comparator . Une explication détaillée des lambdas et Comparator peut être trouvée ici, et une chronique sur le tri et les applications de Comparator peut être trouvée ici.

Dans ce tutoriel, nous explorerons plusieurs fonctions introduites pour l' interface Comparator dans Java 8 .

2. Premiers pas

2.1. Exemple de classe de haricots

Pour les exemples de cet article, créons un bean Employee et utilisons ses champs à des fins de comparaison et de tri:

public class Employee { String name; int age; double salary; long mobile; // constructors, getters & setters }

2.2. Nos données de test

Créons également un tableau d'employés qui sera utilisé pour stocker les résultats de notre type dans divers cas de test tout au long de l'article:

employees = new Employee[] { ... };

La commande initiale des éléments des employés sera:

[Employee(name=John, age=25, salary=3000.0, mobile=9922001), Employee(name=Ace, age=22, salary=2000.0, mobile=5924001), Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

Tout au long de l'article, nous allons trier au-dessus du tableau Employee en utilisant différentes fonctions.

Pour les assertions de test, nous utiliserons un ensemble de tableaux pré-triés que nous comparerons à nos résultats de tri (c'est-à-dire le tableau des employés ) pour différents scénarios.

Déclarons quelques-uns de ces tableaux:

@Before public void initData() { sortedEmployeesByName = new Employee[] {...}; sortedEmployeesByNameDesc = new Employee[] {...}; sortedEmployeesByAge = new Employee[] {...}; // ... }

Comme toujours, n'hésitez pas à consulter notre lien GitHub pour le code complet.

3. Utilisation de Comparator.comparing

Cette section couvre les variantes de la fonction statique Comparator.comparing .

3.1. Variante de sélecteur à clé

La fonction statique Comparator.comparing accepte une fonction de clé de tri et renvoie un comparateur pour le type qui contient la clé de tri:

static 
    
      Comparator comparing( Function keyExtractor)
    

Pour voir cela en action, utilisons le champ de nom dans Employee comme clé de tri et transmettons sa référence de méthode en tant qu'argument de type Function. Le comparateur retourné par le même est utilisé pour le tri:

@Test public void whenComparing_thenSortedByName() { Comparator employeeNameComparator = Comparator.comparing(Employee::getName); Arrays.sort(employees, employeeNameComparator); assertTrue(Arrays.equals(employees, sortedEmployeesByName)); }

Comme vous pouvez le voir, les valeurs du tableau des employés sont triées par nom à la suite du tri:

[Employee(name=Ace, age=22, salary=2000.0, mobile=5924001), Employee(name=John, age=25, salary=3000.0, mobile=9922001), Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)] 

3.2. Sélecteur à clé et variante de comparateur

Il existe une autre option qui facilite le remplacement de l'ordre naturel de la clé de tri en fournissant le comparateur qui crée un ordre personnalisé pour la clé de tri:

static  Comparator comparing( Function keyExtractor, Comparator keyComparator)

Modifions le test ci-dessus, en remplaçant l'ordre naturel de tri par le champ de nom en fournissant un comparateur pour trier les noms par ordre décroissant comme deuxième argument de Comparator.comparing :

@Test public void whenComparingWithComparator_thenSortedByNameDesc() { Comparator employeeNameComparator = Comparator.comparing( Employee::getName, (s1, s2) -> { return s2.compareTo(s1); }); Arrays.sort(employees, employeeNameComparator); assertTrue(Arrays.equals(employees, sortedEmployeesByNameDesc)); }

Comme vous pouvez le voir, les résultats sont triés par ordre décroissant par nom :

[Employee(name=Keith, age=35, salary=4000.0, mobile=3924401), Employee(name=John, age=25, salary=3000.0, mobile=9922001), Employee(name=Ace, age=22, salary=2000.0, mobile=5924001)]

3.3. Utilisation de Comparator.reversed

Lorsqu'elle est appelée sur un comparateur existant , la méthode d'instance Comparator.reversed renvoie un nouveau comparateur qui inverse l'ordre de tri de l'original.

Utilisons le comparateur qui trie les employés par nom et inversons- le pour que les employés soient triés par ordre décroissant du nom :

@Test public void whenReversed_thenSortedByNameDesc() { Comparator employeeNameComparator = Comparator.comparing(Employee::getName); Comparator employeeNameComparatorReversed = employeeNameComparator.reversed(); Arrays.sort(employees, employeeNameComparatorReversed); assertTrue(Arrays.equals(employees, sortedEmployeesByNameDesc)); }

Les résultats sont triés par ordre décroissant de nom :

[Employee(name=Keith, age=35, salary=4000.0, mobile=3924401), Employee(name=John, age=25, salary=3000.0, mobile=9922001), Employee(name=Ace, age=22, salary=2000.0, mobile=5924001)]

3.4. Utilisation de Comparator.comparingInt

Il existe également une fonction Comparator.comparingInt qui fait la même chose que Comparator.comparing , mais elle ne prend que des sélecteurs int . Essayons ceci avec un exemple où nous classons les employés par âge :

@Test public void whenComparingInt_thenSortedByAge() { Comparator employeeAgeComparator = Comparator.comparingInt(Employee::getAge); Arrays.sort(employees, employeeAgeComparator); assertTrue(Arrays.equals(employees, sortedEmployeesByAge)); }

Voyons comment les valeurs du tableau des employés sont classées après le tri:

[Employee(name=Ace, age=22, salary=2000.0, mobile=5924001), Employee(name=John, age=25, salary=3000.0, mobile=9922001), Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

3.5. Utilisation de Comparator.comparingLong

Semblable à ce que nous avons fait pour les clés int , voyons un exemple utilisant Comparator.comparingLong pour considérer une clé de tri de type long en ordonnant le tableau des employés par le champ mobile :

@Test public void whenComparingLong_thenSortedByMobile() { Comparator employeeMobileComparator = Comparator.comparingLong(Employee::getMobile); Arrays.sort(employees, employeeMobileComparator); assertTrue(Arrays.equals(employees, sortedEmployeesByMobile)); }

Let's see how the employees array values are ordered after the sort with mobile as the key:

[Employee(name=Keith, age=35, salary=4000.0, mobile=3924401), Employee(name=Ace, age=22, salary=2000.0, mobile=5924001), Employee(name=John, age=25, salary=3000.0, mobile=9922001)]

3.6. Using Comparator.comparingDouble

Again, similar to what we did for int and long keys, let's see an example using Comparator.comparingDouble to consider a sort key of type double by ordering the employees array by the salary field:

@Test public void whenComparingDouble_thenSortedBySalary() { Comparator employeeSalaryComparator = Comparator.comparingDouble(Employee::getSalary); Arrays.sort(employees, employeeSalaryComparator); assertTrue(Arrays.equals(employees, sortedEmployeesBySalary)); }

Let's see how the employees array values are ordered after the sort with salary as the sort key:

[Employee(name=Ace, age=22, salary=2000.0, mobile=5924001), Employee(name=John, age=25, salary=3000.0, mobile=9922001), Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

4. Considering Natural Order in Comparator

The natural order is defined by the behavior of the Comparable interface implementation. More information about the difference between Comparator and uses of the Comparable interface can be found in this article.

Let's implement Comparable in our Employee class so that we can try the naturalOrder and reverseOrder functions of the Comparator interface:

public class Employee implements Comparable{ // ... @Override public int compareTo(Employee argEmployee) { return name.compareTo(argEmployee.getName()); } }

4.1. Using Natural Order

The naturalOrder function returns the Comparator for the return type mentioned in the signature:

static 
    
      Comparator naturalOrder()
    

Given the above logic to compare employees based on name field, let's use this function to obtain to a Comparator which sorts the employees array in natural order:

@Test public void whenNaturalOrder_thenSortedByName() { Comparator employeeNameComparator = Comparator. naturalOrder(); Arrays.sort(employees, employeeNameComparator); assertTrue(Arrays.equals(employees, sortedEmployeesByName)); }

Let's see how the employees array values are ordered after the sort:

[Employee(name=Ace, age=22, salary=2000.0, mobile=5924001), Employee(name=John, age=25, salary=3000.0, mobile=9922001), Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

4.2. Using Reverse Natural Order

Similar to naturalOrder, let's use the reverseOrder method to generate a Comparator which will produce a reverse ordering of employees to the one in the naturalOrder example:

@Test public void whenReverseOrder_thenSortedByNameDesc() { Comparator employeeNameComparator = Comparator. reverseOrder(); Arrays.sort(employees, employeeNameComparator); assertTrue(Arrays.equals(employees, sortedEmployeesByNameDesc)); }

Let's see how the employees array values are ordered after the sort:

[Employee(name=Keith, age=35, salary=4000.0, mobile=3924401), Employee(name=John, age=25, salary=3000.0, mobile=9922001), Employee(name=Ace, age=22, salary=2000.0, mobile=5924001)]

5. Considering Null Values in Comparator

This section covers functions nullsFirst and nullsLast, which consider null values in ordering and keep the null values at the beginning or end of the ordering sequence.

5.1. Considering Null First

Let's randomly insert null values in employees array:

[Employee(name=John, age=25, salary=3000.0, mobile=9922001), null, Employee(name=Ace, age=22, salary=2000.0, mobile=5924001), null, Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

The nullsFirst function will return a Comparator that keeps all nulls at the beginning of the ordering sequence:

@Test public void whenNullsFirst_thenSortedByNameWithNullsFirst() { Comparator employeeNameComparator = Comparator.comparing(Employee::getName); Comparator employeeNameComparator_nullFirst = Comparator.nullsFirst(employeeNameComparator); Arrays.sort(employeesArrayWithNulls, employeeNameComparator_nullFirst); assertTrue(Arrays.equals( employeesArrayWithNulls, sortedEmployeesArray_WithNullsFirst)); }

Let's see how the employees array values are ordered after the sort:

[null, null, Employee(name=Ace, age=22, salary=2000.0, mobile=5924001), Employee(name=John, age=25, salary=3000.0, mobile=9922001), Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

5.2. Considering Null Last

The nullsLast function will return a Comparator that keeps all nulls at the end of the ordering sequence:

@Test public void whenNullsLast_thenSortedByNameWithNullsLast() { Comparator employeeNameComparator = Comparator.comparing(Employee::getName); Comparator employeeNameComparator_nullLast = Comparator.nullsLast(employeeNameComparator); Arrays.sort(employeesArrayWithNulls, employeeNameComparator_nullLast); assertTrue(Arrays.equals( employeesArrayWithNulls, sortedEmployeesArray_WithNullsLast)); }

Let's see how the employees array values are ordered after the sort:

[Employee(name=Ace, age=22, salary=2000.0, mobile=5924001), Employee(name=John, age=25, salary=3000.0, mobile=9922001), Employee(name=Keith, age=35, salary=4000.0, mobile=3924401), null, null]

6. Using Comparator.thenComparing

The thenComparing function lets you set up lexicographical ordering of values by provisioning multiple sort keys in a particular sequence.

Let's consider another array of Employee class:

someMoreEmployees = new Employee[] { ... };

Consider the following sequence of elements in the above array:

[Employee(name=Jake, age=25, salary=3000.0, mobile=9922001), Employee(name=Jake, age=22, salary=2000.0, mobile=5924001), Employee(name=Ace, age=22, salary=3000.0, mobile=6423001), Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

Let's write a sequence of comparisons as age followed by the name and see the ordering of this array:

@Test public void whenThenComparing_thenSortedByAgeName(){ Comparator employee_Age_Name_Comparator = Comparator.comparing(Employee::getAge) .thenComparing(Employee::getName); Arrays.sort(someMoreEmployees, employee_Age_Name_Comparator); assertTrue(Arrays.equals(someMoreEmployees, sortedEmployeesByAgeName)); }

Here the ordering will be done by age, and for the values with the same age, ordering will be done by name. Let's observe this in the sequence we receive after sorting:

[Employee(name=Ace, age=22, salary=3000.0, mobile=6423001), Employee(name=Jake, age=22, salary=2000.0, mobile=5924001), Employee(name=Jake, age=25, salary=3000.0, mobile=9922001), Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

Let's use the other version of thenComparing that is thenComparingInt, by changing the lexicographical sequence to name followed by age:

@Test public void whenThenComparing_thenSortedByNameAge() { Comparator employee_Name_Age_Comparator = Comparator.comparing(Employee::getName) .thenComparingInt(Employee::getAge); Arrays.sort(someMoreEmployees, employee_Name_Age_Comparator); assertTrue(Arrays.equals(someMoreEmployees, sortedEmployeesByNameAge)); }

Let's see how the employees array values are ordered after the sort:

[Employee(name=Ace, age=22, salary=3000.0, mobile=6423001), Employee(name=Jake, age=22, salary=2000.0, mobile=5924001), Employee(name=Jake, age=25, salary=3000.0, mobile=9922001), Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

Similarly, there are functions thenComparingLong and thenComparingDouble for using long and double sorting keys.

7. Conclusion

Cet article est un guide de plusieurs fonctionnalités introduites dans Java 8 pour l' interface Comparator .

Comme d'habitude, le code source peut être trouvé sur Github.