Útmutató a Java 8 gyűjtőihez

1. Áttekintés

Ebben az oktatóanyagban áttekintjük a Java 8 gyűjtőit, amelyeket a a feldolgozás utolsó lépésében használunk Folyam.

Ha többet szeretne tudni erről Folyam Maga az API, ellenőrizze ezt a cikket.

Ha meg szeretné tudni, hogyan lehet kihasználni a Collectors párhuzamos feldolgozáshoz szükséges energiáját, ellenőrizze ezt a projektet.

2. A Stream.collect () Módszer

Stream.collect () a Java 8-ok egyike Stream APITerminális módszerei. Ez lehetővé teszi számunkra, hogy módosítható hajtási műveleteket hajtsunk végre (egyes elemek újracsomagolása egyes adatstruktúrákba és további logikák alkalmazása, összefűzése stb.) A Folyam példa.

Ennek a műveletnek a stratégiáját a (z) Gyűjtő interfész megvalósítása.

3. Gyűjtők

Minden előre definiált megvalósítás megtalálható a Gyűjtők osztály. Általános gyakorlat, hogy a következő statikus importálást használják velük a megnövelt olvashatóság érdekében:

importáljon statikus java.util.stream.Collectors. *;

vagy csak az Ön által választott egyetlen import gyűjtők:

statikus importálás: java.util.stream.Collectors.toList; statikus importálás: java.util.stream.Collectors.toMap; statikus importálás: java.util.stream.Collectors.toSet;

A következő példákban a következő listát használjuk fel újra:

List givenList = Arrays.asList ("a", "bb", "ccc", "dd");

3.1. Collectors.toList ()

ToList gyűjtő használható az összes összegyűjtésére Folyam elemeket a Lista példa. A legfontosabb dolog, amire emlékezni kell, az a tény, hogy nem tételezhetünk fel különösebbet Lista megvalósítása ezzel a módszerrel. Ha jobban szeretné ellenőrizni ezt, használja a toCollection helyette.

Hozzunk létre egy Folyam egy elemsorozatot reprezentáló példány, és gyűjtse össze őket a Lista példa:

Lista eredménye = megadottList.stream () .collect (toList ());

3.1.1. Collectors.toUnmodifiableList ()

A Java 10 egy kényelmes módszert vezetett be a Folyam elemeket módosíthatatlanná Lista:

Lista eredménye = megadottList.stream () .collect (toUnmodifiableList ());

Ha most megpróbáljuk módosítani a eredményLista, kapunk egy UnsupportedOperationException:

assertThatThrownBy (() -> result.add ("foo")) .isInstanceOf (UnsupportedOperationException.class);

3.2. Collectors.toSet ()

Beállít gyűjtő használható az összes összegyűjtésére Folyam elemeket a Készlet példa. A legfontosabb dolog, amire emlékezni kell, az a tény, hogy nem tételezhetünk fel különösebbet Készlet megvalósítása ezzel a módszerrel. Ha ezt jobban akarjuk ellenőrizni, használhatjuk toCollection helyette.

Hozzunk létre egy Folyam egy elemsorozatot reprezentáló példány, és gyűjtse össze őket a Készlet példa:

Set eredmény = megadottList.stream () .collect (toSet ());

A Készlet nem tartalmaz ismétlődő elemeket. Ha gyűjteményünk egymással egyenlő elemeket tartalmaz, akkor azok az eredményként megjelennek Készlet csak egyszer:

List listWithDuplicates = Arrays.asList ("a", "bb", "c", "d", "bb"); Set result = listWithDuplicates.stream (). Collect (toSet ()); assertThat (eredmény) .hasSize (4);

3.2.1. Collectors.toUnmodifiableSet ()

A Java 10 óta könnyen létrehozhatunk egy módosíthatatlant Készlet felhasználásával toUnmodifiableSet () gyűjtő:

Set eredmény = megadottList.stream () .collect (toUnmodifiableSet ());

Bármilyen kísérlet a eredmény Set a végén lesz UnsupportedOperationException:

assertThatThrownBy (() -> result.add ("foo")) .isInstanceOf (UnsupportedOperationException.class);

3.3. Collectors.toCollection ()

Mint valószínűleg már észrevette, a használat során toSet és toList gyűjtők, nem tehet feltételezéseket a megvalósításukról. Ha egyéni megvalósítást szeretne használni, akkor a toCollection gyűjtő egy választott kollekcióval.

Hozzunk létre egy Folyam egy elemsorozatot reprezentáló példány, és gyűjtse össze őket a LinkedList példa:

Lista eredménye = megadottList.stream () .collect (toCollection (LinkedList :: új))

Figyelje meg, hogy ez egyetlen megváltoztathatatlan gyűjtemény esetén sem fog működni. Ebben az esetben vagy meg kell írnia egy szokást Gyűjtő megvalósítás vagy felhasználás gyűjtésÉs aztán.

3.4. Gyűjtők.térképre()

Térképre gyűjtővel lehet gyűjteni Folyam elemeket a Térkép példa. Ehhez két funkciót kell biztosítanunk:

  • keyMapper
  • valueMapper

keyMapper kivonására szolgál a Térkép kulcs a Folyam elem, és valueMapper egy adott kulccsal társított érték kibontására szolgál.

Gyűjtsük össze ezeket az elemeket a Térkép amely a karakterláncokat kulcsként tárolja, hosszuk pedig értékként:

Térkép eredménye = megadottList.stream () .collect (toMap (Funkcióazonosító (), Karakterlánc :: hossz))

Funkció.azonosság () csak egy parancsikon egy függvény definiálására, amely ugyanazt az értéket fogadja el és adja vissza.

Mi történik, ha gyűjteményünk duplikált elemeket tartalmaz? Ellentétes beállít, térképre nem szűri némán a duplikátumokat. Érthető - hogyan kell kitalálni, hogy melyik értéket válassza ehhez a kulcshoz?

List listWithDuplicates = Arrays.asList ("a", "bb", "c", "d", "bb"); assertThatThrownBy (() -> {listWithDuplicates.stream (). collect (toMap (Function.identity (), String :: length));}). isInstanceOf (IllegalStateException.class);

Vegye figyelembe, hogy térképre nem is értékeli, hogy az értékek is megegyeznek-e. Ha megismétli a kulcsokat, azonnal dob egy IllegalStateException.

Ilyen esetekben kulcsütközés esetén használnunk kell térképre másik aláírással:

Térkép eredménye = megadottList.stream () .collect (toMap (Funkció.azonosító (), Karakterlánc :: hossz, (elem, azonos elem) -> elem));

A harmadik érv itt a BinaryOperator, ahol megadhatjuk, hogy miként akarjuk kezelni az ütközéseket. Ebben az esetben csak a két ütköző érték bármelyikét választjuk, mert tudjuk, hogy ugyanazok a húrok is mindig azonos hosszúságúak lesznek.

3.4.1. Collectors.toUnmodifiableMap ()

Hasonlóan, mint a Listas és Készlets, a Java 10 bemutatta a gyűjtés egyszerű módját Folyam elemeket módosíthatatlanná Térkép:

Térkép eredménye = megadottList.stream () .collect (toMap (Funkcióazonosító (), Karakterlánc :: hossz))

Mint láthatjuk, ha megpróbálunk új bejegyzést tenni a eredménytérkép, megkapjuk UnsupportedOperationException:

assertThatThrownBy (() -> result.put ("foo", 3)) .isInstanceOf (UnsupportedOperationException.class);

3.5. Gyűjtők.collectingAndThen ()

ÖsszegyűjtésAkkor egy speciális gyűjtő, amely lehetővé teszi egy másik művelet végrehajtását az eredményen közvetlenül a végek gyűjtése után.

Gyűjtsük össze Folyam elemek a Lista példányt, majd konvertálja az eredményt egy ImmutableList példa:

Lista eredménye = megadottList.stream () .collect (gyűjtésAndThen (toList (), ImmutableList :: copyOf))

3.6. Gyűjtők.joining ()

Csatlakozás kollektor használható a csatlakozáshoz Folyam elemek.

Csatlakozhatunk hozzájuk azáltal, hogy:

Karakterlánc eredménye = megadottList.stream () .collect (csatlakozás ());

ami:

"abbcccdd"

Megadhat egyéni elválasztókat, előtagokat, utótagokat is:

Karakterlánc eredménye = megadottList.stream () .collect (csatlakozás (""));

ami:

"a bb ccc dd"

vagy írhat:

Karakterlánc eredménye = megadottList.stream () .collect (csatlakozás ("", "PRE-", "-POST"));

ami:

"PRE-a bb ccc dd-POST"

3.7. Gyűjtők.cszámlálás ()

Számolás egy egyszerű gyűjtő, amely lehetővé teszi az összes egyszerű számlálását Folyam elemek.

Most írhatunk:

Hosszú eredmény = megadottList.stream () .collect (számlálás ());

3.8. Gyűjtők.summarizingDupla / Hosszú / Int ()

ÖsszefoglalvaDupla / Hosszú / Int egy gyűjtő, amely egy speciális osztályt ad vissza, amely statisztikai információkat tartalmaz a numerikus adatokról a Folyam kivont elemekből.

Információt szerezhetünk a húrok hosszáról:

DoubleSummaryStatistics eredmény = megadottList.stream () .collect (summarizingDouble (String :: hossz));

Ebben az esetben a következők lesznek igazak:

assertThat (eredmény.getAverage ()). isEqualTo (2); assertThat (eredmény.getCount ()). isEqualTo (4); assertThat (eredmény.getMax ()). isEqualTo (3); assertThat (eredmény.getMin ()). isEqualTo (1); assertThat (eredmény.getSum ()). isEqualTo (8);

3.9. Collectors.averagingDupla / Hosszú / Int ()

ÁtlagolásKettős / Hosszú / Int olyan gyűjtő, amely egyszerűen kinyomtatja a kivont elemek átlagát.

Az átlagos húrhosszat az alábbiak szerint kaphatjuk meg:

Dupla eredmény = megadottList.stream () .collect (averagingDouble (String :: hossz));

3.10. Gyűjtők.summingDupla / Hosszú / Int ()

ÖsszegezveDupla / Hosszú / Int olyan gyűjtő, amely egyszerűen csak a kivont elemek összegét adja vissza.

Az összes húrhossz összegét az alábbiak szerint kaphatjuk meg:

Dupla eredmény = megadottList.stream () .collect (summingDouble (String :: hossz));

3.11. Gyűjtők.maxBy () / minBy ()

MaxBy/MinBy a gyűjtők az a legnagyobb / legkisebb elemét adják vissza Folyam feltéve szerint Összehasonlító példa.

Kiválaszthatjuk a legnagyobb elemet azáltal, hogy:

Opcionális eredmény = megadottList.stream () .collect (maxBy (Comparator.naturalOrder ()));

Figyelje meg, hogy a visszatérő érték egy Választható példa. Ez arra kényszeríti a felhasználókat, hogy gondolják át az üres gyűjtősarok esetét.

3.12. Gyűjtők.groupingBy ()

GroupingBy A gyűjtőt az objektumok egyes tulajdonságok szerinti csoportosításához és az eredmények a Térkép példa.

Csoportosíthatjuk őket karakterlánc-hosszúság szerint, és a csoportosítási eredményeket tároljuk Készlet példányok:

Térkép eredmény = megadottList.stream () .collect (groupingBy (String :: hossz, toSet ()));

Ez azt eredményezi, hogy a következők igazak:

assertThat (eredmény) .containsEntry (1, newHashSet ("a")) .containsEntry (2, newHashSet ("bb", "dd")) .containsEntry (3, newHashSet ("ccc")); 

Figyeljük meg, hogy a csoportosításBy módszer a Gyűjtő és szabadon használhat bármelyiket Gyűjtő az Ön választása szerint.

3.13. Collectors.partitioningBy ()

Partíciózás speciális esete csoportosításBy hogy elfogadja a Állítmány példány és gyűjt Folyam elemeket a Térkép tárolja Logikai értékek kulcsként és gyűjtemények értékként. Az „igaz” kulcs alatt az adott elemnek megfelelő elemek gyűjteménye található Állítmány, és a „hamis” kulcs alatt olyan elemek gyűjteménye található, amelyek nem felelnek meg a megadottnak Állítmány.

Tudsz írni:

Térkép eredmény = megadottList.stream () .collect (particionálásBy (s -> s.hossz ()> 2))

Ennek eredményeként egy térkép tartalmaz:

{false = ["a", "bb", "dd"], true = ["ccc"]} 

3.14. Collectors.teeing ()

Keressük meg az adott maximális és minimális számot Folyam az eddig tanult gyűjtők felhasználásával:

Lista számok = tömbök. AsList (42, 4, 2, 24); Választható min = számok.folyam (). Gyűjt (minBy (Egész szám: összehasonlítTo)); Opcionális max = számok.folyam (). Gyűjt (maxBy (Egész :: Összehasonlít)); // csinálj valami hasznosat min és max értékekkel

Itt két különböző gyűjtőt használunk, majd e kettő eredményét ötvözve létrehozunk valami értelmes dolgot. A Java 12 előtt az ilyen használati esetek lefedése érdekében meg kellett operálnunk a megadottakat Folyam kétszer tárolja a köztes eredményeket ideiglenes változókba, majd ezeket az eredményeket egyesíti.

Szerencsére a Java 12 egy beépített gyűjtőt kínál, amely a nevünkben gondoskodik ezekről a lépésekről: mindössze annyit kell tennünk, hogy a két gyűjtőt és az egyesítő funkciót biztosítsuk.

Mivel ez az új gyűjtő az adott folyamot két különböző irányba tereli, így hívják teeing:

számok.stream (). gyűjt (teeing (minBy (Egész szám: összehasonlít), // Az első gyűjtő maxBy (Egész szám: összehasonlít gyűjtők és ötvözi őket));

Ez a példa a GitHubon érhető el a core-java-12 projektben.

4. Egyedi gyűjtők

Ha meg akarja írni a Collector megvalósítását, akkor be kell vezetnie a Collector felületet, és meg kell határoznia annak három általános paraméterét:

nyilvános felület Gyűjtő {...}
  1. T - a gyűjtésre rendelkezésre álló tárgyak típusa,
  2. A - egy módosítható akkumulátor objektum típusa,
  3. R - a végeredmény típusa.

Írjunk egy példát a Collector-ról az elemek gyűjtésére egy ImmutableSet példa. A megfelelő típusok megadásával kezdjük:

privát osztály Az ImmutableSetCollector megvalósítja a Collector alkalmazást {...}

Mivel a belső gyűjtési műveletek kezeléséhez változtatható gyűjteményre van szükségünk, nem tudjuk használni ImmutableSet ezért; használnunk kell valamilyen más mutábilis gyűjteményt vagy bármilyen más osztályt, amely ideiglenesen felhalmozhat számunkra tárgyakat.

Ebben az esetben folytatjuk a ImmutableSet.Builder és most 5 módszert kell végrehajtanunk:

  • Támogatótámogató()
  • BiConsumerakkumulátor()
  • BinaryOperatorkombinátor()
  • Funkcióbefejező()
  • Készlet jellemzők()

A szállító ()metódus a Támogató példány, amely üres akkumulátorpéldányt generál, ezért ebben az esetben egyszerűen írhatjuk:

@ Nyilvános beszállító felülírása szállító () {return ImmutableSet :: építő; } 

Akkumulátor () A method egy olyan függvényt ad vissza, amelyet új elem hozzáadásához használnak egy meglévőhöz akkumulátor objektumot, ezért használjuk csak a Építész’S hozzá módszer.

@Orride public BiConsumer accumulator () {return ImmutableSet.Builder :: add; }

A kombinátor ()A method egy olyan függvényt ad vissza, amelyet két akkumulátor összevonására használnak:

@Orride public BinaryOperator combiner () {return (balra, jobbra) -> left.addAll (right.build ()); }

A befejező () A method egy olyan függvényt ad vissza, amelyet az akkumulátor végeredménytípussá alakításához használunk, ezért ebben az esetben csak a következőket fogjuk használni Építész’S épít módszer:

@Orride public Function finisher () {return ImmutableSet.Builder :: build; }

A jellegzetességek() metódus arra szolgál, hogy a Stream számára további információkat nyújtson, amelyeket a belső optimalizáláshoz használnak fel. Ebben az esetben nem figyelünk az a-ban rendezett elemekre Készlet hogy használni fogjuk Jellemzők.RENDELETTLEN. A témával kapcsolatos további információkért ellenőrizze Jellemzők’JavaDoc.

@A nyilvános halmaz jellemzőinek felülbírálása () {return Sets.immutableEnumSet (Characteristics.UNORDERED); }

Itt van a teljes megvalósítás a használat mellett:

public class Az ImmutableSetCollector megvalósítja a Collector alkalmazást {@Orride public Supplier szállító () {return ImmutableSet :: építő; } @Orride public BiConsumer accumulator () {return ImmutableSet.Builder :: add; } @Orride public BinaryOperator combiner () {return (balra, jobbra) -> left.addAll (right.build ()); } @Orride public Function finisher () {return ImmutableSet.Builder :: build; } @Override public A készlet jellemzői () {return Sets.immutableEnumSet (Characteristics.UNORDERED); } public static ImmutableSetCollector toImmutableSet () {return new ImmutableSetCollector (); }

és itt cselekményben:

List givenList = Arrays.asList ("a", "bb", "ccc", "dddd"); ImmutableSet eredmény = megadottList.stream () .collect (toImmutableSet ());

5. Következtetés

Ebben a cikkben mélyreható Java 8-kat tártunk fel Gyűjtők és megmutatta az egyik megvalósítását. Ellenőrizze az egyik projektemet, amely javítja a Java párhuzamos feldolgozásának képességeit.

Minden kódpélda elérhető a GitHubon. További érdekes cikkeket olvashat az oldalamon.