Guide de Java 8 Comparator.comparing ()

Guide de Java 8 Comparator.comparing ()

1. Vue d'ensemble

Java 8 a introduit plusieurs améliorations à l'interfaceComparator, 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'interfaceComparator. Une explication détaillée des lambdas et desComparator peut être trouvéehere, et une chronique sur le tri et les applications deComparator peut être trouvéehere.

Dans ce didacticiel,we will explore several functions introduced for the Comparator interface in Java 8.

2. Commencer

2.1. Exemple de classe de haricots

Pour les exemples de cet article, créons un beanEmployee 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 seront utilisés pour stocker les résultats de notre type dans divers cas de test tout au long de l’article:

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

L'ordre initial des éléments deemployees 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 tableauEmployee à l'aide de 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 tableauemployees) 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 nosGitHub link pour le code complet.

3. Utilisation deComparator.comparing

Cette section couvre les variantes de la fonction statiqueComparator.comparing.

3.1. Variante de sélecteur de clé

La fonction statiqueComparator.comparing accepte une clé de triFunction et renvoie unComparator pour le type qui contient la clé de tri:

static > Comparator comparing(
   Function keyExtractor)

Pour voir cela en action, utilisons le champname dansEmployee comme clé de tri et passons sa référence de méthode en tant qu'argument de typeFunction. LesComparator renvoyés 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 tableauemployees 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 varianteComparator

Il existe une autre option qui facilite le remplacement de l'ordre naturel de la clé de tri en fournissant lesComparator qui créent 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 champname en fournissant unComparator pour trier les noms par ordre décroissant comme deuxième argument deComparator.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 dename:

[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 deComparator.reversed

Lorsqu'elle est appelée sur unComparator existant, la méthode d'instanceComparator.reversed renvoie un nouveauComparator qui inverse l'ordre de tri de l'original.

Utilisons leComparator qui trie les employés parname etreverse it afin que les employés soient triés par ordre décroissant desname:

@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 dename:

[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 deComparator.comparingInt

Il existe également une fonctionComparator.comparingInt qui fait la même chose queComparator.comparing, mais elle ne prend que les sélecteursint. Essayons ceci avec un exemple où nous classonsemployees parage:

@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 tableauemployees 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 deComparator.comparingLong

Semblable à ce que nous avons fait pour les clésint, voyons un exemple utilisantComparator.comparingLong pour considérer une clé de tri de typelong en ordonnant le tableauemployees par lemobilechamp s:

@Test
public void whenComparingLong_thenSortedByMobile() {
    Comparator employeeMobileComparator
      = Comparator.comparingLong(Employee::getMobile);

    Arrays.sort(employees, employeeMobileComparator);

    assertTrue(Arrays.equals(employees, sortedEmployeesByMobile));
}

Voyons comment les valeurs du tableauemployees sont classées après le tri avecmobile comme clé:

[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. Utilisation deComparator.comparingDouble

Encore une fois, similaire à ce que nous avons fait pour les clésint etlong, voyons un exemple utilisantComparator.comparingDouble pour considérer une clé de tri de typedouble en ordonnant leemployees tableau par le champsalary:

@Test
public void whenComparingDouble_thenSortedBySalary() {
    Comparator employeeSalaryComparator
      = Comparator.comparingDouble(Employee::getSalary);

    Arrays.sort(employees, employeeSalaryComparator);

    assertTrue(Arrays.equals(employees, sortedEmployeesBySalary));
}

Voyons comment les valeurs du tableauemployees sont classées après le tri avecsalary comme clé de 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)]

4. Considérer l'ordre naturel enComparator

L'ordre naturel est défini par le comportement de l'implémentation de l'interfaceComparable. Plus d'informations sur la différence entreComparator et les utilisations de l'interfaceComparable peuvent être trouvéesin this article.

ImplémentonsComparable dans notre classeEmployee afin que nous puissions essayer les fonctionsnaturalOrder etreverseOrder de l'interfaceComparator:

public class Employee implements Comparable{
    // ...

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

4.1. Utilisation de l'ordre naturel

La fonctionnaturalOrder renvoie lesComparator pour le type de retour mentionné dans la signature:

static > Comparator naturalOrder()

Étant donné la logique ci-dessus pour comparer les employés en fonction du champname, utilisons cette fonction pour obtenir unComparator qui trie le tableauemployees dans l'ordre naturel:

@Test
public void whenNaturalOrder_thenSortedByName() {
    Comparator employeeNameComparator
      = Comparator. naturalOrder();

    Arrays.sort(employees, employeeNameComparator);

    assertTrue(Arrays.equals(employees, sortedEmployeesByName));
}

Voyons comment les valeurs du tableauemployees 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)]

4.2. Utilisation de l'ordre naturel inverse

Similaire ànaturalOrder, utilisons la méthodereverseOrder pour générer unComparator qui produira un ordre inverse deemployees par rapport à celui de l'exemplenaturalOrder:

@Test
public void whenReverseOrder_thenSortedByNameDesc() {
    Comparator employeeNameComparator
      = Comparator. reverseOrder();

    Arrays.sort(employees, employeeNameComparator);

    assertTrue(Arrays.equals(employees, sortedEmployeesByNameDesc));
}

Voyons comment les valeurs du tableauemployees sont classées après le tri:

[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. Prise en compte des valeurs nulles dans le comparateur

Cette section couvre les fonctionsnullsFirst etnullsLast, qui considèrent les valeursnull dans l'ordre et conservent les valeursnull au début ou à la fin de la séquence de commande.

5.1. Considérer Null d'abord

Insérons aléatoirement les valeursnull dans le tableauemployees:

[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)]

La fonctionnullsFirst renverra unComparator qui garde tous lesnulls au début de la séquence de classement:

@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));
}

Voyons comment les valeurs du tableauemployees sont classées après le tri:

[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. Considérer Null Last

La fonctionnullsLast renverra unComparator qui conserve tous lesnulls à la fin de la séquence de classement:

@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));
}

Voyons comment les valeurs du tableauemployees 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),
null,
null]

6. Utilisation deComparator.thenComparing

La fonctionthenComparing vous permet de configurer l'ordre lexicographique des valeurs en fournissant plusieurs clés de tri dans une séquence particulière.

Considérons un autre tableau de la classeEmployee:

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

Considérons la séquence d'éléments suivante dans le tableau ci-dessus:

[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)]

Écrivons une séquence de comparaisons sous la formeage suivi desname et voyons l’ordre de ce tableau:

@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));
}

Ici le classement sera fait parage, et pour les valeurs avec les mêmesage, le classement sera fait parname. Observons ceci dans la séquence que nous recevons après le tri:

[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)]

Utilisons l’autre version dethenComparing qui estthenComparingInt, en changeant la séquence lexicographique enname suivi deage:

@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));
}

Voyons comment les valeurs du tableauemployees sont classées après le tri:

[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)]

De même, il existe des fonctionsthenComparingLong etthenComparingDouble pour utiliser les clés de trilong etdouble.

7. Conclusion

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

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