Anleitung zu Java 8 Comparator.comparing ()

Anleitung zu Java 8 Comparator.comparing ()

1. Überblick

Java 8 führte mehrere Verbesserungen an derComparator-Schnittstelle ein, darunter eine Handvoll statischer Funktionen, die bei der Erstellung einer Sortierreihenfolge für Sammlungen von großem Nutzen sind.

Java 8-Lambdas können auch mit derComparator-Schnittstelle effektiv genutzt werden. Eine ausführliche Erklärung von Lambdas undComparator finden Sie inhere, und eine Chronik zur Sortierung und Anwendung vonComparator finden Sie inhere.

In diesem Tutorial werdenwe will explore several functions introduced for the Comparator interface in Java 8.

2. Anfangen

2.1. Beispiel Bohnenklasse

Für die Beispiele in diesem Artikel erstellen wir eineEmployee-Bohne und verwenden ihre Felder zum Vergleichen und Sortieren:

public class Employee {
    String name;
    int age;
    double salary;
    long mobile;

    // constructors, getters & setters
}

2.2. Unsere Testdaten

Erstellen wir auch eine Reihe von Mitarbeitern, mit denen die Ergebnisse unseres Typs in verschiedenen Testfällen im gesamten Artikel gespeichert werden:

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

Die anfängliche Reihenfolge der Elemente vonemployees lautet:

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

Im gesamten Artikel werden wir das Array überEmployeemit verschiedenen Funktionen sortieren.

Für Testzusicherungen verwenden wir eine Reihe vorsortierter Arrays, die wir für verschiedene Szenarien mit unseren Sortierergebnissen (d. H. Dememployees-Array) vergleichen.

Lassen Sie uns einige dieser Arrays deklarieren:

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

    // ...
}

Wie immer können Sie unsereGitHub link für den vollständigen Code verwenden.

3. Verwenden vonComparator.comparing

Dieser Abschnitt behandelt Varianten der statischen Funktion vonComparator.comparing.

3.1. Schlüsselauswahlvariante

Die statische FunktionComparator.comparingakzeptiert einen SortierschlüsselFunction und gibtComparator für den Typ zurück, der den Sortierschlüssel enthält:

static > Comparator comparing(
   Function keyExtractor)

Um dies in Aktion zu sehen, verwenden wir das Feldname inEmployee als Sortierschlüssel und übergeben die Methodenreferenz als Argument vom TypFunction.. DieComparator werden von demselben zurückgegeben wird zum Sortieren verwendet:

@Test
public void whenComparing_thenSortedByName() {
    Comparator employeeNameComparator
      = Comparator.comparing(Employee::getName);

    Arrays.sort(employees, employeeNameComparator);

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

Wie Sie sehen können, werden die Array-Werte vonemployeesals Ergebnis der Sortierung nach Namen sortiert:

[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. Tastenauswahl undComparator Variante

Es gibt eine weitere Option, die das Überschreiben der natürlichen Reihenfolge des Sortierschlüssels erleichtert, indemComparator angegeben werden, mit der eine benutzerdefinierte Reihenfolge für den Sortierschlüssel erstellt wird:

static  Comparator comparing(
  Function keyExtractor,
    Comparator keyComparator)

Ändern Sie den obigen Test, indem Sie die natürliche Sortierreihenfolge nach dem Feldname überschreiben, indem SieComparator zum Sortieren der Namen in absteigender Reihenfolge als zweites Argument fürComparator.comparing angeben:

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

Wie Sie sehen können, werden die Ergebnisse in absteigender Reihenfolge nachname sortiert:

[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. Verwenden vonComparator.reversed

Beim Aufrufen eines vorhandenenComparator gibt die InstanzmethodeComparator.reversed ein neuesComparator zurück, das die Sortierreihenfolge des Originals umkehrt.

Verwenden wir dieComparator, die die Mitarbeiter nachname undreverse sortieren, damit die Mitarbeiter in absteigender Reihenfolge dername sortiert werden:

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

Die Ergebnisse sind in absteigender Reihenfolge nachname sortiert:

[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. Verwenden vonComparator.comparingInt

Es gibt auch eine FunktionComparator.comparingInt, die dasselbe tut wieComparator.comparing, aber nurint Selektoren benötigt. Versuchen wir dies anhand eines Beispiels, in dem wiremployees nachage ordnen:

@Test
public void whenComparingInt_thenSortedByAge() {
    Comparator employeeAgeComparator
      = Comparator.comparingInt(Employee::getAge);

    Arrays.sort(employees, employeeAgeComparator);

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

Mal sehen, wie die Array-Werte vonemployeesnach der Sortierung geordnet sind:

[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. Verwenden vonComparator.comparingLong

Ähnlich wie beiint Schlüsseln sehen wir uns ein Beispiel mitComparator.comparingLong an, um einen Sortierschlüssel vom Typlong zu betrachten, indem das Arrayemployees nachmobilegeordnet wird. s Feld:

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

    Arrays.sort(employees, employeeMobileComparator);

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

Mal sehen, wie die Array-Werte vonemployeesnach der Sortierung mitmobile als Schlüssel sortiert werden:

[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. Verwenden vonComparator.comparingDouble

Ähnlich wie bei den Schlüsselnint undlong sehen wir uns ein Beispiel an, in demComparator.comparingDouble verwendet wird, um einen Sortierschlüssel vom Typdouble zu betrachten, indememployeesbestellt wird. s Array durch das Feldsalary:

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

    Arrays.sort(employees, employeeSalaryComparator);

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

Mal sehen, wie die Array-Werte vonemployeesnach der Sortierung mitsalary als Sortierschlüssel sortiert werden:

[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. Berücksichtigung der natürlichen Ordnung inComparator

Die natürliche Reihenfolge wird durch das Verhalten der Schnittstellenimplementierung vonComparabledefiniert. Weitere Informationen zum Unterschied zwischenComparator und der Verwendung derComparable-Schnittstelle finden Sie unterin this article.

Implementieren wirComparable in unserer KlasseEmployee, damit wir die FunktionennaturalOrder undreverseOrder derComparator-Schnittstelle ausprobieren können:

public class Employee implements Comparable{
    // ...

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

4.1. Natürliche Ordnung nutzen

Die FunktionnaturalOrder gibtComparator für den in der Signatur genannten Rückgabetyp zurück:

static > Comparator naturalOrder()

Angesichts der obigen Logik zum Vergleichen von Mitarbeitern basierend auf dem Feldnameverwenden wir diese Funktion, um einComparator zu erhalten, das das Arrayemployeesin natürlicher Reihenfolge sortiert:

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

    Arrays.sort(employees, employeeNameComparator);

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

Mal sehen, wie die Array-Werte vonemployeesnach der Sortierung geordnet sind:

[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. Verwenden der umgekehrten natürlichen Reihenfolge

Verwenden Sie ähnlich wie beinaturalOrder die MethodereverseOrder, umComparator zu generieren, wodurch eine umgekehrte Reihenfolge vonemployees zu der im Beispiel vonnaturalOrder erzeugt wird:

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

    Arrays.sort(employees, employeeNameComparator);

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

Mal sehen, wie die Array-Werte vonemployeesnach der Sortierung geordnet sind:

[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. Berücksichtigung von Nullwerten im Komparator

Dieser Abschnitt behandelt die FunktionennullsFirst undnullsLast, dienull Werte bei der Bestellung berücksichtigen und dienull Werte am Anfang oder Ende der Bestellsequenz beibehalten.

5.1. Zuerst Null betrachten

Fügen wir zufällignull Werte inemployees Array ein:

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

Die FunktionnullsFirst gibtComparator zurück, wodurch allenulls am Anfang der Bestellsequenz bleiben:

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

Mal sehen, wie die Array-Werte vonemployeesnach der Sortierung geordnet sind:

[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. Betrachtet man Null Last

Die FunktionnullsLast gibtComparator zurück, wodurch allenulls am Ende der Bestellsequenz bleiben:

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

Mal sehen, wie die Array-Werte vonemployeesnach der Sortierung geordnet sind:

[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. Verwenden vonComparator.thenComparing

Mit der FunktionthenComparing können Sie die lexikografische Reihenfolge der Werte festlegen, indem Sie mehrere Sortierschlüssel in einer bestimmten Reihenfolge bereitstellen.

Betrachten wir ein weiteres Array der KlasseEmployee:

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

Betrachten Sie die folgende Abfolge von Elementen im obigen 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)]

Schreiben wir eine Folge von Vergleichen alsage, gefolgt vonname, und sehen uns die Reihenfolge dieses Arrays an:

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

Hier erfolgt die Bestellung nachage, und für die Werte mit denselbenage erfolgt die Bestellung nachname. Beobachten wir dies in der Reihenfolge, die wir nach dem Sortieren erhalten:

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

Verwenden wir die andere Version vonthenComparing, nämlichthenComparingInt, indem wir die lexikografische Reihenfolge inname gefolgt vonage ändern:

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

Mal sehen, wie die Array-Werte vonemployeesnach der Sortierung geordnet sind:

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

Ebenso gibt es FunktionenthenComparingLong undthenComparingDouble zur Verwendung der Sortiertastenlong unddouble.

7. Fazit

Dieser Artikel enthält eine Anleitung zu verschiedenen Funktionen, die in Java 8 für dieComparator-Schnittstelle eingeführt wurden.

Wie üblich kann der Quellcodeover on Github gefunden werden.