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.