1. Überblick

Java 8 - Leistungsstarker Vergleich mit Lambdas

1. Überblick

In diesem Tutorial werfen wir einen ersten Blick auf dieLambda support in Java 8 – specifically at how to leverage it to write the Comparator and sort a Collection.

Dieser Artikel ist Teil vonthe “Java – Back to Basic” series hier am Beispiel.

Weitere Lektüre:

Das Java 8 Stream API Tutorial

Der Artikel ist eine beispielhafte Einführung in die Möglichkeiten und Vorgänge der Java 8 Stream-API.

Read more

Leitfaden für Java 8-Collectors

Der Artikel behandelt Java 8-Collectors und zeigt Beispiele für integrierte Collectors sowie das Erstellen benutzerdefinierter Collectors.

Read more

Lambda-Ausdrücke und funktionale Schnittstellen: Tipps und bewährte Methoden

Tipps und bewährte Methoden zur Verwendung von Java 8-Lambdas und funktionalen Schnittstellen.

Read more

Definieren wir zunächst eine einfache Entitätsklasse:

public class Human {
    private String name;
    private int age;

    // standard constructors, getters/setters, equals and hashcode
}

2. Grundsortierung ohne Lambdas

Vor Java 8 würde das Sortieren einer Sammlungcreating an anonymous inner class for the Comparator umfassen, die für die Sortierung verwendet werden:

new Comparator() {
    @Override
    public int compare(Human h1, Human h2) {
        return h1.getName().compareTo(h2.getName());
    }
}

Dies würde einfach verwendet, um dieList vonHuman Entitäten zu sortieren:

@Test
public void givenPreLambda_whenSortingEntitiesByName_thenCorrectlySorted() {
    List humans = Lists.newArrayList(
      new Human("Sarah", 10),
      new Human("Jack", 12)
    );

    Collections.sort(humans, new Comparator() {
        @Override
        public int compare(Human h1, Human h2) {
            return h1.getName().compareTo(h2.getName());
        }
    });
    Assert.assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}

3. Grundlegende Sortierung mit Lambda-Unterstützung

Mit der Einführung von Lambdas können wir nun die anonyme innere Klasse umgehen und mitsimple, functional semantics dasselbe Ergebnis erzielen:

(final Human h1, final Human h2) -> h1.getName().compareTo(h2.getName());

In ähnlicher Weise können wir das Verhalten jetzt wie zuvor testen:

@Test
public void whenSortingEntitiesByName_thenCorrectlySorted() {
    List humans = Lists.newArrayList(
      new Human("Sarah", 10),
      new Human("Jack", 12)
    );

    humans.sort(
      (Human h1, Human h2) -> h1.getName().compareTo(h2.getName()));

    assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}

Beachten Sie, dass wir auchthe new sort API added to java.util.List in Java 8 verwenden - anstelle der altenCollections.sort-API.

4. Grundlegende Sortierung ohne Typdefinitionen

Wir können den Ausdruck weiter vereinfachen, indem wir die Typdefinitionen -the compiler is capable of inferring these nicht alleine angeben:

(h1, h2) -> h1.getName().compareTo(h2.getName())

Auch hier bleibt der Test sehr ähnlich:

@Test
public void
  givenLambdaShortForm_whenSortingEntitiesByName_thenCorrectlySorted() {

    List humans = Lists.newArrayList(
      new Human("Sarah", 10),
      new Human("Jack", 12)
    );

    humans.sort((h1, h2) -> h1.getName().compareTo(h2.getName()));

    assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}

5. Sortieren mit Verweis auf statische Methode

Als Nächstes führen wir die Sortierung mit einem Lambda-Ausdruck unter Bezugnahme auf eine statische Methode durch.

Zuerst definieren wir die MethodecompareByNameThenAge - mit genau der gleichen Signatur wie diecompare-Methode in einemComparator<Human>-Objekt:

public static int compareByNameThenAge(Human lhs, Human rhs) {
    if (lhs.name.equals(rhs.name)) {
        return lhs.age - rhs.age;
    } else {
        return lhs.name.compareTo(rhs.name);
    }
}

Jetzt rufen wir die Methodehumans.sortmit dieser Referenz auf:

humans.sort(Human::compareByNameThenAge);

Das Endergebnis ist eine funktionierende Sortierung der Sammlung unter Verwendung der statischen Methode alsComparator:

@Test
public void
  givenMethodDefinition_whenSortingEntitiesByNameThenAge_thenCorrectlySorted() {

    List humans = Lists.newArrayList(
      new Human("Sarah", 10),
      new Human("Jack", 12)
    );

    humans.sort(Human::compareByNameThenAge);
    Assert.assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}

6. Extrahierte Komparatoren sortieren

Wir können auch vermeiden, selbst die Vergleichslogik selbst zu definieren, indem wir eineinstance method reference- und dieComparator.comparing-Methode verwenden, die basierend auf dieser FunktionComparable extrahiert und erstellt.

Wir werden den GettergetName() verwenden, um den Lambda-Ausdruck zu erstellen und die Liste nach Namen zu sortieren:

@Test
public void
  givenInstanceMethod_whenSortingEntitiesByName_thenCorrectlySorted() {

    List humans = Lists.newArrayList(
      new Human("Sarah", 10),
      new Human("Jack", 12)
    );

    Collections.sort(
      humans, Comparator.comparing(Human::getName));
    assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}

7. Reverse Sort

JDK 8 hat auch eine Hilfsmethode fürreversing the comparator eingeführt - wir können diese schnell nutzen, um unsere Sortierung umzukehren:

@Test
public void whenSortingEntitiesByNameReversed_thenCorrectlySorted() {
    List humans = Lists.newArrayList(
      new Human("Sarah", 10),
      new Human("Jack", 12)
    );

    Comparator comparator
      = (h1, h2) -> h1.getName().compareTo(h2.getName());

    humans.sort(comparator.reversed());

    Assert.assertThat(humans.get(0), equalTo(new Human("Sarah", 10)));
}

8. Sortieren mit mehreren Bedingungen

Die Vergleichs-Lambda-Ausdrücke müssen nicht so einfach sein - wir könnenmore complex expressions as well schreiben - zum Beispiel die Entitäten zuerst nach Namen und dann nach Alter sortieren:

@Test
public void whenSortingEntitiesByNameThenAge_thenCorrectlySorted() {
    List humans = Lists.newArrayList(
      new Human("Sarah", 12),
      new Human("Sarah", 10),
      new Human("Zack", 12)
    );

    humans.sort((lhs, rhs) -> {
        if (lhs.getName().equals(rhs.getName())) {
            return lhs.getAge() - rhs.getAge();
        } else {
            return lhs.getName().compareTo(rhs.getName());
        }
    });
    Assert.assertThat(humans.get(0), equalTo(new Human("Sarah", 10)));
}

9. Sortieren mit mehreren Bedingungen - Zusammensetzung

Dieselbe Vergleichslogik - zuerst nach Namen und dann nach Alter sortieren - kann auch durch die neue Kompositionsunterstützung fürComparator implementiert werden.

Starting with JDK 8, we can now chain together multiple comparators, um eine komplexere Vergleichslogik zu erstellen:

@Test
public void
  givenComposition_whenSortingEntitiesByNameThenAge_thenCorrectlySorted() {

    List humans = Lists.newArrayList(
      new Human("Sarah", 12),
      new Human("Sarah", 10),
      new Human("Zack", 12)
    );

    humans.sort(
      Comparator.comparing(Human::getName).thenComparing(Human::getAge)
    );

    Assert.assertThat(humans.get(0), equalTo(new Human("Sarah", 10)));
}

10. Sortieren einer Liste mitStream.sorted()

Wir können eine Sammlung auch mit derStreamsorted()API von Java 8 sortieren.

Wir können den Stream sowohl nach natürlicher Reihenfolge als auch nach Reihenfolge sortieren, die durchComparator. bereitgestellt wird. Dazu haben wir zwei überladene Varianten dersorted()-API:

  • sorted() sortiert die Elemente vonStream in natürlicher Reihenfolge; Die Elementklasse muss dieComparable-Schnittstelle implementieren.

  • sorted(Comparator<?superT>comparator) - sortiert die Elemente basierend auf einerComparator-Instanz

Sehen wir uns ein Beispiel füruse the sorted() method with natural ordering an:

@Test
public final void
  givenStreamNaturalOrdering_whenSortingEntitiesByName_thenCorrectlySorted() {
    List letters = Lists.newArrayList("B", "A", "C");

    List sortedLetters = letters.stream().sorted().collect(Collectors.toList());
    assertThat(sortedLetters.get(0), equalTo("A"));
}

Nun wollen wir sehen, wie wiruse a custom Comparator with the sorted() API können:

@Test
public final void
  givenStreamCustomOrdering_whenSortingEntitiesByName_thenCorrectlySorted() {
    List humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));
    Comparator nameComparator = (h1, h2) -> h1.getName().compareTo(h2.getName());

    List sortedHumans =
      humans.stream().sorted(nameComparator).collect(Collectors.toList());
    assertThat(sortedHumans.get(0), equalTo(new Human("Jack", 12)));
}

Wir können das obige Beispiel noch weiter vereinfachen, wenn wiruse the Comparator.comparing() method:

@Test
public final void
  givenStreamComparatorOrdering_whenSortingEntitiesByName_thenCorrectlySorted() {
    List humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));

    List sortedHumans = humans.stream()
      .sorted(Comparator.comparing(Human::getName))
      .collect(Collectors.toList());

    assertThat(sortedHumans.get(0), equalTo(new Human("Jack", 12)));
}

11. Sortieren einer Liste in umgekehrter Reihenfolge mitStream.sorted()

Wir können auchStream.sorted() verwenden, um eine Sammlung in umgekehrter Reihenfolge zu sortieren.

Schauen wir uns zunächst ein Beispiel fürcombine the sorted()method with Comparator.reverseOrder() to sort a list in reverse natural order an:

@Test
public final void
  givenStreamNaturalOrdering_whenSortingEntitiesByNameReversed_thenCorrectlySorted() {
    List letters = Lists.newArrayList("B", "A", "C");

    List reverseSortedLetters = letters.stream()
      .sorted(Comparator.reverseOrder())
      .collect(Collectors.toList());

    assertThat(reverseSortedLetters.get(0), equalTo("C"));
}

Nun wollen wir sehen, wie wiruse the sorted() method and a custom Comparator können:

@Test
public final void
  givenStreamCustomOrdering_whenSortingEntitiesByNameReversed_thenCorrectlySorted() {
    List humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));
    Comparator reverseNameComparator =
      (h1, h2) -> h2.getName().compareTo(h1.getName());

    List reverseSortedHumans = humans.stream().sorted(reverseNameComparator)
      .collect(Collectors.toList());
    assertThat(reverseSortedHumans.get(0), equalTo(new Human("Sarah", 10)));
}

Beachten Sie, dass der Aufruf voncompareTo umgedreht wird, was die Umkehrung bewirkt.

Lassen Sie uns abschließend das obige Beispiel umusing the Comparator.comparing() method vereinfachen:

@Test
public final void
  givenStreamComparatorOrdering_whenSortingEntitiesByNameReversed_thenCorrectlySorted() {
    List humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));

    List reverseSortedHumans = humans.stream()
      .sorted(Comparator.comparing(Human::getName, Comparator.reverseOrder()))
      .collect(Collectors.toList());

    assertThat(reverseSortedHumans.get(0), equalTo(new Human("Sarah", 10)));
}

12. Fazit

Dieser Artikel illustrierte die verschiedenen und aufregenden Wege, auf denen aList can be sorted using Java 8 Lambda Expressions - direkt an syntaktischem Zucker vorbei und in eine echte und leistungsstarke funktionale Semantik übergeht.

Die Implementierung all dieser Beispiele und Codefragmentecan be found in the GitHub project - dies ist ein Eclipse-basiertes Projekt, daher sollte es einfach zu importieren und auszuführen sein, wie es ist.