Inleiding tot Protonpack

1. Overzicht

In deze tutorial kijken we naar de belangrijkste kenmerken van Protonpack, een bibliotheek die de standaard uitbreidt Stroom API door wat aanvullende functionaliteit toe te voegen.

Raadpleeg dit artikel hier om de grondbeginselen van Java te ontdekken Stroom API.

2. Maven Afhankelijkheid

Om de Protonpack-bibliotheek te gebruiken, moeten we een afhankelijkheid toevoegen in onze pom.xml het dossier:

 com.codepoetics protonpack 1.15 

Controleer de nieuwste versie op Maven Central.

3. StreamUtils

Dit is de hoofdklasse die Java's standaard uitbreidt Stroom API.

Alle hier besproken methoden zijn tussenliggende bewerkingen, wat betekent dat ze een Stroom maar activeert de verwerking ervan niet.

3.1. takeWhile () en takeUntil ()

takeWhile () haalt waarden uit de bronstroom zolang ze voldoen aan de geleverde voorwaarde:

Stream streamOfInt = Stream .iterate (1, i -> i + 1); Lijstresultaat = StreamUtils .takeWhile (streamOfInt, i -> i <5) .collect (Collectors.toList ()); assertThat (resultaat). bevat (1, 2, 3, 4);

Omgekeerd, takeUntil () neemt waarden totdat een waarde voldoet aan de geleverde voorwaarde en stopt dan:

Stream streamOfInt = Stream .iterate (1, i -> i + 1); Lijstresultaat = StreamUtils .takeUntil (streamOfInt, i -> i> = 5) .collect (Collectors.toList ()); assertThat (resultaat) .containsExactly (1, 2, 3, 4);

In Java 9 en verder, takeWhile () maakt deel uit van de norm Stroom API.

3.2. zip ()

zip () neemt twee of drie streams als input en een combinerfunctie. De methode neemt een waarde van dezelfde positie van elke stream en geeft deze door aan de combiner.

Het doet dit totdat een van de streams geen waarden meer heeft:

String [] clubs = {"Juventus", "Barcelona", "Liverpool", "PSG"}; String [] players = {"Ronaldo", "Messi", "Salah"}; Zet zippedFrom2Sources = StreamUtils .zip (stream (clubs), stream (spelers), (club, speler) -> club + "" + speler) .collect (Collectors.toSet ()); assertThat (zippedFrom2Sources) .contains ("Juventus Ronaldo", "Barcelona Messi", "Liverpool Salah"); 

Evenzo een overbelast zip () waarvoor drie bronnenstroom nodig is:

String [] competities = {"Serie A", "La Liga", "Premier League"}; Set zippedFrom3Sources = StreamUtils .zip (stream (clubs), stream (spelers), stream (competities), (club, speler, competitie) -> club + "" + speler + "" + competitie) .collect (Collectors.toSet ( )); assertThat (zippedFrom3Sources) .bevat ("Juventus Ronaldo Serie A", "Barcelona Messi La Liga", "Liverpool Salah Premier League");

3.3. zipWithIndex ()

zipWithIndex () neemt waarden en zip elke waarde met zijn index om een ​​stroom geïndexeerde waarden te maken:

Stream streamOfClubs = Stream .of ("Juventus", "Barcelona", "Liverpool"); Set zipsWithIndex = StreamUtils .zipWithIndex (streamOfClubs) .collect (Collectors.toSet ()); assertThat (zipsWithIndex) .contains (Indexed.index (0, "Juventus"), Indexed.index (1, "Barcelona"), Indexed.index (2, "Liverpool"));

3.4. samenvoegen()

samenvoegen() werkt met meerdere source streams en een combiner. Het neemt de waarde van dezelfde indexpositie uit elke source stream en geeft deze door aan de combiner.

De methode werkt door achtereenvolgens 1 waarde uit dezelfde index uit elke stream te halen, beginnend bij de zaad waarde.

Vervolgens wordt de waarde doorgegeven aan de combiner, en de resulterende gecombineerde waarde wordt teruggevoerd naar de combiner om de volgende waarde te creëren:

Stream streamOfClubs = Stream .of ("Juventus", "Barcelona", "Liverpool", "PSG"); Stream streamOfPlayers = Stream .of ("Ronaldo", "Messi", "Salah"); Stream streamOfLeagues = Stream .of ("Serie A", "La Liga", "Premier League"); Set merged = StreamUtils.merge (() -> "", (valOne, valTwo) -> valOne + "" + valTwo, streamOfClubs, streamOfPlayers, streamOfLeagues) .collect (Collectors.toSet ()); assertThat (samengevoegd) .bevat ("Juventus Ronaldo Serie A", "Barcelona Messi La Liga", "Liverpool Salah Premier League", "PSG");

3.5. mergeToList ()

mergeToList () neemt meerdere streams als invoer. Het combineert de waarde van dezelfde index van elke stream tot een Lijst:

Stream streamOfClubs = Stream .of ("Juventus", "Barcelona", "PSG"); Stream streamOfPlayers = Stream .of ("Ronaldo", "Messi"); Stroom mergedStreamOfList = StreamUtils .mergeToList (streamOfClubs, streamOfPlayers); Lijst mergedListOfList = mergedStreamOfList .collect (Collectors.toList ()); assertThat (mergedListOfList.get (0)) .containsExactly ("Juventus", "Ronaldo"); assertThat (mergedListOfList.get (1)) .containsExactly ("Barcelona", "Messi"); assertThat (mergedListOfList.get (2)) .containsExactly ("PSG");

3.6. interleave ()

interleave ()maakt alternatieve waarden uit meerdere streams met behulp van een selector.

De methode geeft een set met één waarde van elke stream naar de selector, en de selector zal een waarde selecteren.

Vervolgens wordt de geselecteerde waarde uit de set verwijderd en vervangen door de volgende waarde waaruit de geselecteerde waarde afkomstig is. Deze iteratie gaat door totdat alle bronnen geen waarden meer hebben.

Het volgende voorbeeld gebruikt interleave () om afwisselende waarden te creëren met een round-robin strategie:

Stream streamOfClubs = Stream .of ("Juventus", "Barcelona", "Liverpool"); Stream streamOfPlayers = Stream .of ("Ronaldo", "Messi"); Stream streamOfLeagues = Stream .of ("Serie A", "La Liga"); Lijst interleavedList = StreamUtils .interleave (Selectors.roundRobin (), streamOfClubs, streamOfPlayers, streamOfLeagues) .collect (Collectors.toList ()); assertThat (interleavedList) .hasSize (7) .containsExactly ("Juventus", "Ronaldo", "Serie A", "Barcelona", "Messi", "La Liga", "Liverpool"); 

Houd er rekening mee dat de bovenstaande code bedoeld is voor zelfstudiedoeleinden, omdat round-robin selector wordt verzorgd door de bibliotheek als Selectors.roundRobin ().

3.7. skipUntil () en skipWhile ()

skipUntil () slaat de waarden over totdat een waarde aan de voorwaarde voldoet:

Geheel getal [] getallen = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; Lijst skippedUntilGreaterThan5 = StreamUtils .skipUntil (stream (nummers), i -> i> 5) .collect (Collectors.toList ()); assertThat (skippedUntilGreaterThan5) .containsExactly (6, 7, 8, 9, 10); 

In tegenstelling tot, skipWhile ()slaat de waarden over terwijl de waarden aan de voorwaarde voldoen:

Geheel getal [] getallen = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; Lijst skippedWhileLessThanEquals5 = StreamUtils .skipWhile (stream (nummers), i -> i <= 5 ||) .collect (Collectors.toList ()); assertThat (skippedWhileLessThanEquals5) .containsExactly (6, 7, 8, 9, 10); 

Een belangrijk ding over skipWhile () is dat het doorgaat met streamen nadat het de eerste waarde heeft gevonden die niet aan de voorwaarde voldoet:

Lijst skippedWhileGreaterThan5 = StreamUtils .skipWhile (stream (nummers), i -> i> 5) .collect (Collectors.toList ()); assertThat (skippedWhileGreaterThan5) .containsExactly (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 

In Java 9 en verder, dropWhile() in standaard Stroom API biedt dezelfde functionaliteit als skipWhile ().

3.8. ontvouwen()

ontvouwen() genereert een potentieel oneindige stream door een aangepaste generator toe te passen op een seed-waarde en vervolgens op elke gegenereerde waarde - de stream kan worden beëindigd door terug te keren Optioneel. Leeg ():

Stroom uitgevouwen = StreamUtils .unfold (2, i -> (i <100)? Optioneel.of (i * i): Optioneel.empty ()); assertThat (unfolded.collect (Collectors.toList ())) .containsExactly (2, 4, 16, 256);

3.9. met vensters ()

met vensters ()maakt meerdere subsets van source stream als een stream van Lijst. De methode gebruikt een source stream, venstergrootte en waarde overslaan als parameter.

De Lijst lengte is gelijk aan venstergrootte, terwijl skip waarde bepaalt waar de subset begint ten opzichte van de vorige subset:

Geheel getal [] getallen = {1, 2, 3, 4, 5, 6, 7, 8}; List windowedWithSkip1 = StreamUtils .windowed (stream (nummers), 3, 1) .collect (Collectors.toList ()); assertThat (windowedWithSkip1) .containsExactly (asList (1, 2, 3), asList (2, 3, 4), asList (3, 4, 5), asList (4, 5, 6), asList (5, 6, 7 )); 

Bovendien heeft het laatste raam gegarandeerd de gewenste maat, zoals we kunnen zien in het volgende voorbeeld:

List windowedWithSkip2 = StreamUtils.windowed (stream (nummers), 3, 2) .collect (Collectors.toList ()); assertThat (windowedWithSkip2) .containsExactly (asList (1, 2, 3), asList (3, 4, 5), asList (5, 6, 7)); 

3.10. aggregaat()

Er zijn er twee aggregaat() methoden die heel anders werken.

De eerste aggregaat() groepeert elementen van gelijke waarde volgens een bepaald predikaat:

Geheel getal [] getallen = {1, 2, 2, 3, 4, 4, 4, 5}; Lijst geaggregeerd = StreamUtils .aggregate (Arrays.stream (nummers), (int1, int2) -> int1.compareTo (int2) == 0) .collect (Collectors.toList ()); assertThat (geaggregeerd) .containsExactly (asList (1), asList (2, 2), asList (3), asList (4, 4, 4), asList (5)); 

Het predikaat ontvangt de waarden aaneengesloten. Daarom geeft het bovenstaande een ander resultaat als het nummer niet is besteld.

Aan de andere kant, de tweede aggregaat() is gewoon gewend groepeer elementen uit de source stream in groepen van de gewenste grootte:

Lijst aggregatedFixSize = StreamUtils .aggregate (stream (nummers), 5) .collect (Collectors.toList ()); assertThat (aggregatedFixSize) .containsExactly (asList (1, 2, 2, 3, 4), asList (4, 4, 5)); 

3.11. aggregateOnListCondition ()

aggregateOnListCondition () groepeert waarden gebaseerd op predikaat en huidige actieve groep. Het predikaat krijgt de momenteel actieve groep als een Lijst en de volgende waarde. Vervolgens moet hij bepalen of de groep moet doorgaan of een nieuwe groep moet beginnen.

In het volgende voorbeeld wordt een vereiste opgelost om aaneengesloten gehele waarden in een groep te groeperen, waarbij de som van de waarden in elke groep niet groter mag zijn dan 5:

Geheel getal [] getallen = {1, 1, 2, 3, 4, 4, 5}; Stroom aggregated = StreamUtils .aggregateOnListCondition (stream (nummers), (currentList, nextInt) -> currentList.stream (). mapToInt (Integer :: intValue) .sum () + nextInt <= 5); assertThat (geaggregeerd) .containsExactly (asList (1, 1, 2), asList (3), asList (4), asList (4), asList (5));

4. Streambaar

Een exemplaar van Stroom is niet herbruikbaar. Om deze reden, Streambaar biedt herbruikbare streams door dezelfde methoden in te pakken en bloot te leggen als de Stroom:

Streamable s = Streamable.of ("a", "b", "c", "d"); Lijst verzameld1 = s.collect (Collectors.toList ()); Lijst verzameld2 = s.collect (Collectors.toList ()); assertThat (verzameld1) .hasSize (4); assertThat (verzameld2) .hasSize (4);

5. CollectorUtils

CollectorUtils vormt een aanvulling op de norm Verzamelaars door verschillende handige verzamelmethoden toe te voegen.

5.1. maxBy () en minBy ()

maxBy ()vindt de maximale waarde in een stream met behulp van de meegeleverde projectielogica:

Streamclubs = Stream.of ("Juventus", "Barcelona", "PSG"); Optioneel longestName = clubs.collect (CollectorUtils.maxBy (String :: length)); assertThat (longestName) .contains ("Barcelona");

In tegenstelling tot, minBy ()vindt de minimumwaarde met behulp van de meegeleverde projectielogica.

5.2. uniek()

De uniek() verzamelaar doet iets heel eenvoudigs: het retourneert de enige waarde als een gegeven stream exact 1 element heeft:

Stream singleElement = Stream.of (1); Optioneel uniek = singleElement.collect (CollectorUtils.unique ()); assertThat (uniek) .contains (1); 

Anders, uniek() zal een uitzondering genereren:

Stream multipleElement = Stream.of (1, 2, 3); assertThatExceptionOfType (NonUniqueValueException.class) .isThrownBy (() -> {multipleElement.collect (CollectorUtils.unique ());}); 

6. Conclusie

In dit artikel hebben we geleerd hoe de Protonpack-bibliotheek de Java Stream API uitbreidt om het gebruik gemakkelijker te maken. Het voegt handige methoden toe die we mogelijk vaak gebruiken, maar die ontbreken in de standaard-API.

Vanaf Java 9 is een deel van de functionaliteit van Protonpack beschikbaar in de standaard Stream API.

Zoals gewoonlijk is de code te vinden op Github.