Java 8 - Kraftig sammenligning med Lambdas

1. Oversikt

I denne opplæringen skal vi ta en første titt på Lambda-støtte i Java 8 - spesielt om hvordan du kan bruke den til å skrive Komparator og sorter en samling.

Denne artikkelen er en del av "Java - Back to Basic" -serien her på Baeldung.

La oss først definere en enkel enhetsklasse:

offentlig klasse Human {private String name; privat alder; // standardkonstruktører, getters / setters, like og hashcode} 

2. Grunnleggende sortering uten lambdas

Før Java 8 vil sortering av en samling innebære skape en anonym indre klasse for Komparator brukt i sorten:

ny Comparator () {@Override offentlig int sammenligne (Human h1, Human h2) {return h1.getName (). CompareTo (h2.getName ()); }}

Dette vil ganske enkelt brukes til å sortere Liste av Menneskelig enheter:

@Test offentlig ugyldig gittPreLambda_whenSortingEntitiesByName_thenCorrectlySorted () {Liste mennesker = Lists.newArrayList (new Human ("Sarah", 10), new Human ("Jack", 12)); Collections.sort (mennesker, ny komparator () {@ Override offentlig int sammenligne (Human h1, Human h2) {return h1.getName (). CompareTo (h2.getName ());}}); Assert.assertThat (mennesker.get (0), likeTo (nytt menneske ("Jack", 12))); }

3. Grunnleggende sortering med Lambda-støtte

Med innføringen av Lambdas kan vi nå omgå den anonyme indre klassen og oppnå det samme resultatet med enkel, funksjonell semantikk:

(endelig Human h1, endelig Human h2) -> h1.getName (). sammenlignTo (h2.getName ());

På samme måte - vi kan nå teste oppførselen akkurat som før:

@Test offentlig ugyldig nårSortingEntitiesByName_thenCorrectlySorted () {Liste mennesker = Lists.newArrayList (nytt menneske ("Sarah", 10), nytt menneske ("Jack", 12)); mennesker.sort ((Human h1, Human h2) -> h1.getName (). CompareTo (h2.getName ())); assertThat (mennesker.get (0), likeTo (nytt menneske ("Jack", 12))); }

Legg merke til at vi også bruker den nye sortere API lagt til java.util.Liste i Java 8 - i stedet for det gamle Collections.sort API.

4. Grunnleggende sortering uten typedefinisjoner

Vi kan ytterligere forenkle uttrykket ved ikke å spesifisere typedefinisjonene - kompilatoren er i stand til å utlede disse på egen hånd:

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

Og igjen forblir testen veldig lik:

@Test offentlig ugyldig gittLambdaShortForm_whenSortingEntitiesByName_thenCorrectlySorted () {Liste mennesker = Lists.newArrayList (nytt menneske ("Sarah", 10), nytt menneske ("Jack", 12)); mennesker.sort ((h1, h2) -> h1.getName (). sammenlignTo (h2.getName ())); assertThat (mennesker.get (0), likeTo (nytt menneske ("Jack", 12))); }

5. Sorter ved hjelp av referanse til statisk metode

Deretter skal vi utføre sorteringen ved hjelp av et Lambda-uttrykk med en referanse til en statisk metode.

Først skal vi definere metoden CompareByNameThenAge - med nøyaktig samme signatur som sammenligne metode i en Komparator gjenstand:

offentlig statisk int CompareByNameThenAge (Human lhs, Human rhs) {if (lhs.name.equals (rhs.name)) {return Integer.compare (lhs.age, rhs.age); } annet {returner lhs.name.compareTo (rhs.name); }}

Nå skal vi ringe mennesker. sort metoden med denne referansen:

mennesker.sort (Human :: CompareByNameThenAge);

Sluttresultatet er en arbeidssortering av samlingen ved hjelp av den statiske metoden som en Komparator:

@Test offentlig ugyldighet givenMethodDefinition_whenSortingEntitiesByNameThenAge_thenCorrectlySorted () {Liste mennesker = Lists.newArrayList (nytt menneske ("Sarah", 10), nytt menneske ("Jack", 12)); mennesker.sort (Human :: CompareByNameThenAge); Assert.assertThat (mennesker.get (0), likeTo (nytt menneske ("Jack", 12))); }

6. Sorter ekstraherte komparatorer

Vi kan også unngå å definere selv sammenligningslogikken ved å bruke en forekomst metode referanse og Comparator. Sammenligning metode - som trekker ut og lager en Sammenlignelig basert på den funksjonen.

Vi skal bruke getter getName () for å bygge Lambda-uttrykket og sortere listen etter navn:

@Test offentlig ugyldighet gittInstanceMethod_whenSortingEntitiesByName_thenCorrectlySorted () {Liste mennesker = Lists.newArrayList (nytt menneske ("Sarah", 10), nytt menneske ("Jack", 12)); Collections.sort (mennesker, Comparator.comparing (Human :: getName)); assertThat (mennesker.get (0), likeTo (nytt menneske ("Jack", 12))); }

7. Omvendt sortering

JDK 8 har også introdusert en hjelpermetode for reversere komparatoren - vi kan raskt bruke det for å reversere vår sort:

@Test offentlig ugyldig nårSortingEntitiesByNameReversed_thenCorrectlySorted () {Liste mennesker = Lists.newArrayList (nytt menneske ("Sarah", 10), nytt menneske ("Jack", 12)); Comparator comparator = (h1, h2) -> h1.getName (). CompareTo (h2.getName ()); mennesker.sort (komparator.omvendt ()); Assert.assertThat (mennesker.get (0), likeTo (nytt menneske ("Sarah", 10))); }

8. Sorter etter flere forhold

Sammenligningen lambdauttrykk trenger ikke være så enkelt - vi kan skrive mer komplekse uttrykk også - for eksempel å sortere enhetene først etter navn og deretter etter alder:

@Test offentlig ugyldig nårSortingEntitiesByNameThenAge_thenCorrectlySorted () {Liste mennesker = Lists.newArrayList (new Human ("Sarah", 12), new Human ("Sarah", 10), new Human ("Zack", 12)); mennesker.sort ((lhs, rhs) -> {if (lhs.getName (). tilsvarer (rhs.getName ())) {return Integer.compare (lhs.getAge (), rhs.getAge ());} annet {return lhs.getName (). CompareTo (rhs.getName ());}}); Assert.assertThat (mennesker.get (0), likeTo (nytt menneske ("Sarah", 10))); }

9. Sorter etter flere forhold - sammensetning

Den samme sammenligningslogikken - først sortering etter navn og deretter sekundært etter alder - kan også implementeres av den nye komposisjonsstøtten for Komparator.

Fra og med JDK 8 kan vi nå koble sammen flere komparatorer å bygge mer kompleks sammenligningslogikk:

@Test offentlig ugyldighet gittComposition_whenSortingEntitiesByNameThenAge_thenCorrectlySorted () {Liste mennesker = Lists.newArrayList (nytt menneske ("Sarah", 12), nytt menneske ("Sarah", 10), nytt menneske ("Zack", 12)); mennesker.sort (Comparator.comparing (Human :: getName) .thenComparing (Human :: getAge)); Assert.assertThat (mennesker.get (0), likeTo (nytt menneske ("Sarah", 10))); }

10. Sortere en liste med Stream.sorted ()

Vi kan også sortere en samling ved hjelp av Java 8-er Strømsortert () API.

Vi kan sortere strømmen ved hjelp av naturlig bestilling samt bestilling levert av a Komparator. For dette har vi to overbelastede varianter av sortert () API:

  • sortereed () sorterer elementene i en Strøm ved hjelp av naturlig bestilling; elementklassen må implementere Sammenlignelig grensesnitt.
  • sortert (Comparator super T> comparator) - sorterer elementene basert på en Komparator forekomst

La oss se et eksempel på hvordan bruke sortert () metode med naturlig bestilling:

@Test offentlig endelig ugyldighet gittStreamNaturalOrdering_whenSortingEntitiesByName_thenCorrectlySorted () {List letters = Lists.newArrayList ("B", "A", "C"); List sortedLetters = letters.stream (). Sorted (). Collect (Collectors.toList ()); assertThat (sortedLetters.get (0), equalTo ("A")); }

La oss nå se hvordan vi kan bruk en egendefinert Komparator med sortert () API:

@Test offentlig endelig ugyldighet gittStreamCustomOrdering_whenSortingEntitiesByName_thenCorrectlySorted () {Liste mennesker = Lists.newArrayList (nytt menneske ("Sarah", 10), nytt menneske ("Jack", 12)); Comparator nameComparator = (h1, h2) -> h1.getName (). CompareTo (h2.getName ()); Liste sortedHumans = mennesker.stream (). Sortert (nameComparator) .collect (Collectors.toList ()); assertThat (sortedHumans.get (0), equalTo (new Human ("Jack", 12))); }

Vi kan forenkle eksemplet ovenfor ytterligere hvis vi bruke Comparator.comparing () metode:

@Test offentlig endelig ugyldighet gittStreamComparatorOrdering_whenSortingEntitiesByName_thenCorrectlySorted () {Liste mennesker = Lists.newArrayList (nytt menneske ("Sarah", 10), nytt menneske ("Jack", 12)); Liste sortedHumans = mennesker.stream () .sortert (Comparator.comparing (Human :: getName)) .collect (Collectors.toList ()); assertThat (sortedHumans.get (0), equalTo (new Human ("Jack", 12))); }

11. Sortere en liste i omvendt rekkefølge Stream.sorted ()

Vi kan også bruke Stream.sorted () for å sortere en samling i omvendt rekkefølge.

La oss først se et eksempel på hvordan kombinere sortert () metode med Comparator.reverseOrder () for å sortere en liste i omvendt naturlig rekkefølge:

@Test offentlig endelig ugyldighet gittStreamNaturalOrdering_whenSortingEntitiesByNameReversed_thenCorrectlySorted () {List letters = Lists.newArrayList ("B", "A", "C"); Liste reverseSortedLetters = letters.stream () .sorted (Comparator.reverseOrder ()) .collect (Collectors.toList ()); assertThat (reverseSortedLetters.get (0), equalTo ("C")); }

La oss nå se hvordan vi kan bruke sortert () metode og en skikk Komparator:

@Test offentlig endelig ugyldighet gittStreamCustomOrdering_whenSortingEntitiesByNameReversed_thenCorrectlySorted () {Liste mennesker = Lists.newArrayList (nytt menneske ("Sarah", 10), nytt menneske ("Jack", 12)); Comparator reverseNameComparator = (h1, h2) -> h2.getName (). CompareTo (h1.getName ()); Liste reverseSortedHumans = mennesker.stream (). Sortert (reverseNameComparator) .collect (Collectors.toList ()); assertThat (reverseSortedHumans.get (0), equalTo (new Human ("Sarah", 10))); }

Merk at påkallingen av sammenligne med blir snudd, som er det som gjør reverseringen.

Til slutt, la oss forenkle eksemplet ovenfor med bruker Comparator.comparing () metode:

@Test offentlig endelig ugyldighet gittStreamComparatorOrdering_whenSortingEntitiesByNameReversed_thenCorrectlySorted () {Liste mennesker = Lists.newArrayList (nytt menneske ("Sarah", 10), nytt menneske ("Jack", 12)); Liste reverseSortedHumans = mennesker.stream () .sortert (Comparator.comparing (Human :: getName, Comparator.reverseOrder ())) .collect (Collectors.toList ()); assertThat (reverseSortedHumans.get (0), equalTo (new Human ("Sarah", 10))); }

12. Nullverdier

Så langt implementerte vi vår Komparators på en måte som de ikke kan sortere samlinger som inneholder null verdier. Det vil si hvis samlingen inneholder minst en null elementet, deretter sortere metoden kaster en NullPointerException:

@Test (forventet = NullPointerException.class) offentlig ugyldig gittANullElement_whenSortingEntitiesByName_thenThrowsNPE () {Liste mennesker = Lists.newArrayList (null, nytt menneske ("Jack", 12)); mennesker.sort ((h1, h2) -> h1.getName (). sammenlignTo (h2.getName ())); }

Den enkleste løsningen er å håndtere null verdier manuelt i vår Komparator gjennomføring:

@Test offentlig ugyldig gittANullElement_whenSortingEntitiesByNameManually_thenMovesTheNullToLast () {Liste mennesker = Lists.newArrayList (null, nytt menneske ("Jack", 12), null); mennesker.sort ((h1, h2) -> {if (h1 == null) {retur h2 == null? 0: 1;} ellers hvis (h2 == null) {retur -1;} returner h1.getName ( ) .compareTo (h2.getName ());}); Assert.assertNotNull (mennesker.get (0)); Assert.assertNull (mennesker. Get (1)); Assert.assertNull (mennesker.get (2)); }

Her presser vi alle null elementer mot slutten av samlingen. For å gjøre det, vurderer komparatoren null å være større enn ikke-nullverdier. Når begge er det null, de regnes som likeverdige.

I tillegg vi kan passere alle Komparator det er ikke null-trygt i Comparator.nullsLast () metode og oppnå samme resultat:

@Test offentlig ugyldig gittANullElement_whenSortingEntitiesByName_thenMovesTheNullToLast () {Liste mennesker = Lists.newArrayList (null, nytt menneske ("Jack", 12), null); mennesker.sort (Comparator.nullsLast (Comparator.comparing (Human :: getName))); Assert.assertNotNull (mennesker.get (0)); Assert.assertNull (mennesker. Get (1)); Assert.assertNull (mennesker.get (2)); }

På samme måte kan vi bruke Comparator.nullsFirst () å flytte null elementer mot starten av samlingen:

@Test offentlig ugyldig gittANullElement_whenSortingEntitiesByName_thenMovesTheNullToStart () {Liste mennesker = Lists.newArrayList (null, nytt menneske ("Jack", 12), null); mennesker.sort (Comparator.nullsFirst (Comparator.comparing (Human :: getName))); Assert.assertNull (mennesker.get (0)); Assert.assertNull (mennesker. Get (1)); Assert.assertNotNull (mennesker.get (2)); } 

Det anbefales på det sterkeste å bruke nullsFirst () eller nullsLast () dekoratører, ettersom de er mer fleksible og fremfor alt mer lesbare.

13. Konklusjon

Denne artikkelen illustrerte de forskjellige og spennende måtene som a Listen kan sorteres ved hjelp av Java 8 Lambda Expressions - å flytte rett forbi syntaktisk sukker og inn i ekte og kraftig funksjonell semantikk.

Implementeringen av alle disse eksemplene og kodebiter finner du på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found