Java 8 - Erőteljes összehasonlítás a Lambdas-szal

1. Áttekintés

Ebben az oktatóanyagban először megnézzük a Lambda támogatás a Java 8-ban - konkrétan arról, hogyan lehet kihasználni a Összehasonlító és rendezzen egy gyűjteményt.

Ez a cikk a „Java - Vissza az alapokhoz” sorozat része, itt, Baeldungon.

Először definiáljunk egy egyszerű entitásosztályt:

public class Humán {private String név; privát int kor; // szabványos kivitelezők, szerelők / beállítók, egyenlőek és hashcode} 

2. Alapvető rendezés Lambdas nélkül

A Java 8 előtt a gyűjtemény rendezése magában foglalná névtelen belső osztály létrehozása a Összehasonlító sorrendben használják:

new Comparator () {@Orride public int összehasonlítás (Human h1, Human h2) {return h1.getName (). CompareTo (h2.getName ()); }}

Ez egyszerűen felhasználható a rendezésre Lista nak,-nek Emberi entitások:

@Test public void givenPreLambda_whenSortingEntitiesByName_thenCorrectlySorted () {List people = Lists.newArrayList (new Human ("Sarah", 10), new Human ("Jack", 12)); Collections.sort (emberek, új Comparator () {@Orride public int összehasonlítás (Human h1, Human h2) {return h1.getName (). CompareTo (h2.getName ());}}); Assert.assertThat (emberek.get (0), egyenlőTo (új Ember ("Jack", 12))); }

3. Alapvető rendezés a Lambda támogatással

A Lambdas bevezetésével most megkerülhetjük a névtelen belső osztályt, és ezzel elérhetjük ugyanazt az eredményt egyszerű, funkcionális szemantika:

(végső emberi h1, végső emberi h2) -> h1.getName (). hasonlítsa össze (h2.getName ());

Hasonlóképpen - most is tesztelhetjük a viselkedést, mint korábban:

@Test public void whenSortingEntitiesByName_thenCorrectlySorted () {List people = Lists.newArrayList (new Human ("Sarah", 10), new Human ("Jack", 12)); emberek.válogatás ((emberi h1, emberi h2) -> h1.getName (). összehasonlítTo (h2.getNév ())); assertThat (emberek.get (0), egyenlőTo (új Ember ("Jack", 12))); }

Vegye észre, hogy mi is használjuk az új fajta API hozzáadva ehhez java.util.List a Java 8-ban - a régi helyett Gyűjtemények.rendezés API.

4. Alapvető rendezés típusdefiníciók nélkül

Tovább egyszerűsíthetjük a kifejezést, ha nem adjuk meg a típusdefiníciókat - a fordító képes ezekre következtetni magában:

(h1, h2) -> h1.getName (). hasonlítsa össze (h2.getName ())

És ismét: a teszt továbbra is nagyon hasonló:

@Test public void givenLambdaShortForm_whenSortingEntitiesByName_thenCorrectlySorted () {List people = Lists.newArrayList (new Human ("Sarah", 10), new Human ("Jack", 12)); emberek.rendezés ((h1, h2) -> h1.getName (). CompareTo (h2.getName ())); assertThat (emberek.get (0), egyenlőTo (új Ember ("Jack", 12))); }

5. Rendezés a Statikus módszer hivatkozása alapján

Ezután elvégezzük a rendezést egy Lambda kifejezés segítségével, statikus módszerre hivatkozva.

Először meghatározzuk a módszert CompareByNameThenAge - pontosan ugyanazzal az aláírással, mint a hasonlítsa össze módszer a Összehasonlító tárgy:

public static int CompareByNameThenAge (Humán lhs, Humán rhs) {if (lhs.név.egyenlő (rhs.név)) {return Integer.compare (lhs.age, rhs.age); } else {return lhs.name.compareTo (rhs.name); }}

Most felhívjuk a emberek.válogatni módszer ezzel a hivatkozással:

emberek.válogatás (Ember :: összehasonlítByNameThenAge);

A végeredmény a gyűjtemény működő rendezése a statikus módszerrel, mint a Összehasonlító:

@Test public void givenMethodDefinition_whenSortingEntitiesByNameThenAge_thenCorrectlySorted () {List people = Lists.newArrayList (new Human ("Sarah", 10), new Human ("Jack", 12)); emberek.válogatás (Ember :: összehasonlítByNameThenAge); Assert.assertThat (emberek.get (0), egyenlőTo (új Ember ("Jack", 12))); }

6. A kivont összehasonlítók rendezése

Kerülhetjük még az összehasonlító logika meghatározását is egy példány metódus hivatkozás és a Összehasonlító.összehasonlítás módszer - amely kivonja és létrehozza a Hasonló azon funkció alapján.

Használjuk a gettert getName () a Lambda kifejezés felépítéséhez és a lista név szerinti rendezéséhez:

@Test public void givenInstanceMethod_whenSortingEntitiesByName_thenCorrectlySorted () {List people = Lists.newArrayList (new Human ("Sarah", 10), new Human ("Jack", 12)); Collections.sort (emberek, Comparator.comparing (Human :: getName)); assertThat (emberek.get (0), egyenlőTo (új Ember ("Jack", 12))); }

7. Fordított rendezés

A JDK 8 bevezetett egy segítő módszert is megfordítva az összehasonlítót - ezt gyorsan felhasználhatjuk a rendezés megfordításához:

@Test public void whenSortingEntitiesByNameReversed_thenCorrectlySorted () {List people = Lists.newArrayList (new Human ("Sarah", 10), new Human ("Jack", 12)); Összehasonlító összehasonlító = (h1, h2) -> h1.getName (). CompareTo (h2.getName ()); emberek.válogatás (összehasonlító.fordítva ()); Assert.assertThat (emberek.get (0), egyenlőTo (új Ember ("Sarah", 10))); }

8. Rendezés több feltétellel

A lambda kifejezések összehasonlításának nem kell ilyen egyszerűnek lennie - írhatunk összetettebb kifejezések is - például az entitások rendezése először név, majd életkor szerint:

@Test public void whenSortingEntitiesByNameThenAge_thenCorrectlySorted () {List people = Lists.newArrayList (new Human ("Sarah", 12), new Human ("Sarah", 10), new Human ("Zack", 12)); people.sort ((lhs, rhs) -> {if (lhs.getName (). egyenlő (rhs.getName ())) {return Integer.compare (lhs.getAge (), rhs.getAge ());} else {return lhs.getName (). CompareTo (rhs.getName ());}}); Assert.assertThat (emberek.get (0), egyenlőTo (új Ember ("Sarah", 10))); }

9. Rendezés több feltétellel - összetétel

Ugyanez az összehasonlítási logika - először név szerinti, majd másodsorban életkor szerinti rendezés - megvalósítható a Összehasonlító.

A JDK 8-tól kezdve több komparátort is összekapcsolhatunk összetettebb összehasonlítási logika felépítéséhez:

@Test public void givenComposition_whenSortingEntitiesByNameThenAge_thenCorrectlySorted () {List people = Lists.newArrayList (new Human ("Sarah", 12), new Human ("Sarah", 10), new Human ("Zack", 12)); emberek.válogatás (Összehasonlító.összehasonlítás (Ember :: getName) .majd Összehasonlítás (Ember :: getAge)); Assert.assertThat (emberek.get (0), egyenlőTo (új Ember ("Sarah", 10))); }

10. Lista rendezése a Stream.sorted ()

Gyűjteményeket is rendezhetünk Java 8-asok segítségével Folyamrendezve () API.

Rendezhetjük a folyamot természetes rendezéssel, valamint a Összehasonlító. Ehhez két túlterhelt változatunk van rendezve () API:

  • fajtaed () az a elemeit rendezi Folyam természetes rendezés alkalmazása; az elemosztálynak végre kell hajtania a Hasonló felület.
  • rendezve (Összehasonlító szuper T> comparator) - az a alapján rendezi az elemeket Összehasonlító példa

Lássunk egy példát arra, hogyan kell használja a rendezve () módszer természetes rendezéssel:

@Test public final void givenStreamNaturalOrdering_whenSortingEntitiesByName_thenCorrectlySorted () {Lista betűi = Lists.newArrayList ("B", "A", "C"); Listed sortedLetters = letters.stream (). Sorted (). Collect (Collectors.toList ()); assertThat (sortedLetters.get (0), equalTo ("A")); }

Most nézzük meg, hogyan tudunk használjon szokást Összehasonlító a ... val rendezve () API:

@Test public final void givenStreamCustomOrdering_whenSortingEntitiesByName_thenCorrectlySorted () {List people = Lists.newArrayList (new Human ("Sarah", 10), new Human ("Jack", 12)); Összehasonlító neveComparator = (h1, h2) -> h1.getName (). CompareTo (h2.getName ()); Lista rendezve: Emberek = emberek.folyam (). Rendezve (névKomparátor) .gyűjtés (Gyűjtők.lista ()); assertThat (sortedHumans.get (0), equalTo (új Ember ("Jack", 12))); }

Még egyszerűsíthetjük a fenti példát, ha igen használja a Comparator.comparing () módszer:

@Test public final void givenStreamComparatorOrdering_whenSortingEntitiesByName_thenCorrectlySorted () {List people = Lists.newArrayList (new Human ("Sarah", 10), new Human ("Jack", 12)); List sortedHumans = people.stream () .sorted (Comparator.comparing (Human :: getName)) .collect (Collectors.toList ()); assertThat (sortedHumans.get (0), equalTo (új Ember ("Jack", 12))); }

11. Lista rendezése fordított sorrendben Stream.sorted ()

Használhatjuk is Stream.sorted () hogy fordítva válogasson egy gyűjteményt.

Először nézzünk meg egy példát arra, hogyan kell kombinálja a rendezve () módszerrel Comparator.reverseOrder () hogy egy listát fordított természetes sorrendben rendezzünk:

@Test public final void givenStreamNaturalOrdering_whenSortingEntitiesByNameReversed_thenCorrectlySorted () {List letter = Lists.newArrayList ("B", "A", "C"); List reverseSortedLetters = letters.stream () .sorted (Comparator.reverseOrder ()) .collect (Collectors.toList ()); assertThat (reverseSortedLetters.get (0), egyenlőTo ("C")); }

Most nézzük meg, hogyan tudunk használja a rendezve () módszer és egy szokás Összehasonlító:

@Test public final void givenStreamCustomOrdering_whenSortingEntitiesByNameReversed_thenCorrectlySorted () {List people = Lists.newArrayList (new Human ("Sarah", 10), new Human ("Jack", 12)); Összehasonlító fordítottNévKomparátor = (h1, h2) -> h2.getName (). Összehasonlít (h1.getName ()); Lista reverseSortedHumans = emberek.stream (). Sorted (reverseNameComparator) .collect (Collectors.toList ()); assertThat (reverseSortedHumans.get (0), egyenlőTo (új Ember ("Sarah", 10))); }

Ne feledje, hogy a összehasonlítani megfordul, ami a hátramenetet végzi.

Végül egyszerűsítsük a fenti példát használni a Comparator.comparing () módszer:

@Test public final void givenStreamComparatorOrdering_whenSortingEntitiesByNameReversed_thenCorrectlySorted () {List people = Lists.newArrayList (new Human ("Sarah", 10), new Human ("Jack", 12)); List reverseSortedHumans = people.stream () .sorted (Comparator.comparing (Human :: getName, Comparator.reverseOrder ())) .collect (Collectors.toList ()); assertThat (reverseSortedHumans.get (0), egyenlőTo (új Ember ("Sarah", 10))); }

12. Null Értékek

Eddig megvalósítottuk Összehasonlítós oly módon, hogy nem tudják rendezni a gyűjteményeket nulla értékek. Vagyis ha a gyűjtemény legalább egyet tartalmaz nulla elem, majd a fajta módszer dob a NullPointerException:

@Test (várható = NullPointerException.class) public void givenANullElement_whenSortingEntitiesByName_thenThrowsNPE () {List people = Lists.newArrayList (null, new Human ("Jack", 12)); emberek.válogatás ((h1, h2) -> h1.getName (). CompareTo (h2.getName ())); }

A legegyszerűbb megoldás a nulla értékeket manuálisan Összehasonlító végrehajtás:

@Test public void givenANullElement_whenSortingEntitiesByNameManually_thenMovesTheNullToLast () {List people = Lists.newArrayList (null, new Human ("Jack", 12), null); emberek.válogatás ((h1, h2) -> {if (h1 == null) {return h2 == null? 0: 1;} else if (h2 == null) {return -1;} return h1.getName ( ) .compareTo (h2.getName ());}); Assert.assertNotNull (emberek.get (0)); Assert.assertNull (emberek.get (1)); Assert.assertNull (emberek.get (2)); }

Itt tologatunk mindent nulla elemek a gyűjtemény vége felé. Ehhez az összehasonlító mérlegeli nulla hogy nagyobb legyen, mint a nem null értékek. Amikor mindkettő nulla, egyenlőnek tekintik őket.

Ezenkívül bármelyiket átadhatjuk Összehasonlító hogy nem semleges a Comparator.nullsLast () módszerrel, és ugyanazt az eredményt érje el:

@Test public void givenANullElement_whenSortingEntitiesByName_thenMovesTheNullToLast () {List people = Lists.newArrayList (null, new Human ("Jack", 12), null); emberek.válogatás (Comparator.nullsLast (Comparator.comparing (Human :: getName))); Assert.assertNotNull (emberek.get (0)); Assert.assertNull (emberek.get (1)); Assert.assertNull (emberek.get (2)); }

Hasonlóképpen használhatjuk Comparator.nullsFirst () mozgatni a nulla elemek a gyűjtemény kezdete felé:

@Test public void givenANullElement_whenSortingEntitiesByName_thenMovesTheNullToStart () {List people = Lists.newArrayList (null, new Human ("Jack", 12), null); emberek.válogatás (Comparator.nullsFirst (Comparator.comparing (Human :: getName))); Assert.assertNull (emberek.get (0)); Assert.assertNull (emberek.get (1)); Assert.assertNotNull (emberek.get (2)); } 

Nagyon ajánlott a nullsFirst () vagy nullsLast () dekorátorok, mivel rugalmasabbak és mindenekelőtt olvashatóbbak.

13. Következtetés

Ez a cikk bemutatta azokat a különféle és izgalmas módszereket, amelyek a A lista a Java 8 Lambda Expressions segítségével rendezhető - jobb elmozdulás a szintaktikai cukor felett, valódi és hatékony funkcionális szemantikába.

Ezen példák és kódrészletek megvalósítása megtalálható a GitHub-on.


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