Einführung in Protonpack

Einführung in Protonpack

1. Überblick

In diesem Tutorial werden die Hauptfunktionen vonProtonpack vorgestellt. Hierbei handelt es sich um eine Bibliothek, die den StandardStream API um einige zusätzliche Funktionen erweitert.

Unterthis write-up here finden Sie Informationen zu den Grundlagen der JavaStream-API.

2. Maven-Abhängigkeit

Um die Protonpack-Bibliothek verwenden zu können, müssen wir unsererpom.xml-Datei eine Abhängigkeit hinzufügen:


    com.codepoetics
    protonpack
    1.15

Suchen Sie nach der neuesten Version vonMaven Central.

3. StreamUtils

Dies ist die Hauptklasse, die die Standard-APIStreamvon Java erweitert.

Alle hier diskutierten Methoden sindintermediate operations, was bedeutet, dass sieStream ändern, aber ihre Verarbeitung nicht auslösen.

3.1. takeWhile() undtakeUntil()

takeWhile() übernimmt Werte aus dem Quellstromas long as they meet the supplied condition:

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

Umgekehrt nimmttakeUntil() Werteuntil a value meets supplied condition an und stoppt dann:

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

Ab Java 9 isttakeWhile() Teil des StandardsStream API.

3.2. zip()

zip() verwendet zwei oder drei Streams als Eingabe- und Kombiniererfunktion. Die Methodetakes a value from the same position of each stream and passes it to the combiner.

Dies geschieht so lange, bis einem der Streams die Werte ausgehen:

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

assertThat(zippedFrom2Sources)
  .contains("Juventus Ronaldo", "Barcelona Messi", "Liverpool Salah");

Ebenso ein überladenerzip(), der drei Quellenstrom benötigt:

String[] leagues = { "Serie A", "La Liga", "Premier League" };
Set zippedFrom3Sources = StreamUtils
  .zip(stream(clubs), stream(players), stream(leagues),
    (club, player, league) -> club + " " + player + " " + league)
  .collect(Collectors.toSet());

assertThat(zippedFrom3Sources).contains(
  "Juventus Ronaldo Serie A",
  "Barcelona Messi La Liga",
  "Liverpool Salah Premier League");

3.3. zipWithIndex()

zipWithIndex () Nimmt Werte und komprimiert jeden Wert mit seinem Index, um einen Stream indizierter Werte zu erstellen:

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. merge()

merge() funktioniert mit mehreren Quelldatenströmen und einem Kombinierer. Estakes the value of the same index position from each source stream and passes it to the combiner.

Die Methode verwendet 1 Wert aus demselben Index nacheinander aus jedem Stream, beginnend mit dem Wert vonseed.

Dann wird der Wert an den Kombinierer übergeben, und der resultierende kombinierte Wert wird an den Kombinierer zurückgemeldet, um den nächsten Wert zu erstellen:

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(merged)
  .contains("Juventus Ronaldo Serie A", "Barcelona Messi La Liga",
    "Liverpool Salah Premier League", "PSG");

3.5. mergeToList()

mergeToList() verwendet mehrere Streams als Eingabe. Escombines the value of the same index from each stream into a List:

Stream streamOfClubs = Stream
  .of("Juventus", "Barcelona", "PSG");
Stream streamOfPlayers = Stream
  .of("Ronaldo", "Messi");

Stream> mergedStreamOfList = StreamUtils
  .mergeToList(streamOfClubs, streamOfPlayers);
List> 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()creates alternates values taken from multiple streams using a selector.

Die Methode gibt einen Satz mit einem Wert aus jedem Stream fürselector an, undselector wählt einen Wert aus.

Dann wird der ausgewählte Wert aus dem Satz entfernt und durch den nächsten Wert ersetzt, von dem der ausgewählte Wert stammt. Diese Iteration wird fortgesetzt, bis allen Quellen die Werte ausgehen.

Im nächsten Beispiel werdeninterleave() verwendet, um alternierende Werte mit der Strategie vonround-robinzu erstellen:

Stream streamOfClubs = Stream
  .of("Juventus", "Barcelona", "Liverpool");
Stream streamOfPlayers = Stream
  .of("Ronaldo", "Messi");
Stream streamOfLeagues = Stream
  .of("Serie A", "La Liga");

List 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");

Beachten Sie, dass der obige Code nur zu Lernzwecken dient, da Round-Robinselector von der Bibliothek alsSelectors.roundRobin() bereitgestellt wird.

3.7. skipUntil() andskipWhile()

skipUntil() überspringt die Werteuntil a value meets the condition:

Integer[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
List skippedUntilGreaterThan5 = StreamUtils
  .skipUntil(stream(numbers), i -> i > 5)
  .collect(Collectors.toList());

assertThat(skippedUntilGreaterThan5).containsExactly(6, 7, 8, 9, 10);

Im Gegensatz dazu sindskipWhile()skips the values while the values meet the condition:

Integer[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
List skippedWhileLessThanEquals5 = StreamUtils
  .skipWhile(stream(numbers), i -> i <= 5 || )
  .collect(Collectors.toList());

assertThat(skippedWhileLessThanEquals5).containsExactly(6, 7, 8, 9, 10);

Eine wichtige Sache beiskipWhile() ist, dass das Streaming fortgesetzt wird, nachdem der erste Wert gefunden wurde, der die Bedingung nicht erfüllt:

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

Ab Java 9 bietetdropWhile() in der Standard-APIStream dieselbe Funktionalität wieskipWhile().

3.8. unfold()

unfold() generiert einen potenziell unendlichen Stream, indem ein benutzerdefinierter Generator auf einen Startwert und dann auf jeden generierten Wert angewendet wird. Der Stream kann durch Rückgabe vonOptional.empty(): beendet werden

Stream unfolded = StreamUtils
  .unfold(2, i -> (i < 100)
    ? Optional.of(i * i) : Optional.empty());

assertThat(unfolded.collect(Collectors.toList()))
  .containsExactly(2, 4, 16, 256);

3.9. windowed()

windowed()creates multiple subsets of source stream as a stream of *List*. Die Methode verwendet einen Quellstrom,window size undskip value als Parameter.

Die Länge vonListentsprichtwindowsize, , während skip value bestimmt, wo die Teilmenge relativ zur vorherigen Teilmenge beginnt:

Integer[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8 };

List windowedWithSkip1 = StreamUtils
  .windowed(stream(numbers), 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));

Außerdem hat das letzte Fenster garantiert die gewünschte Größe, wie das folgende Beispiel zeigt:

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

3.10. aggregate()

Es gibt zweiaggregate() -Methoden, die ganz unterschiedlich funktionieren.

Die erstenaggregate() groups together elements of equal value according to a given predicate:

Integer[] numbers = { 1, 2, 2, 3, 4, 4, 4, 5 };
List aggregated = StreamUtils
  .aggregate(Arrays.stream(numbers), (int1, int2) -> int1.compareTo(int2) == 0)
  .collect(Collectors.toList());
assertThat(aggregated).containsExactly(asList(1), asList(2, 2), asList(3), asList(4, 4, 4), asList(5));

Das Prädikat empfängt die Werte zusammenhängend. Aus diesem Grund ergibt sich ein anderes Ergebnis, wenn die Nummer nicht bestellt wurde.

Andererseits wird das zweiteaggregate() einfach zugroup together elements from the source stream into groups of the desired size verwendet:

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

3.11. aggregateOnListCondition()

aggregateOnListCondition() gruppiert Wertebased on predicate and current active group. Das Prädikat erhält die aktuell aktive Gruppe alsList und den nächsten Wert. Anschließend muss festgelegt werden, ob die Gruppe fortgesetzt oder eine neue Gruppe erstellt werden soll.

Das folgende Beispiel löst die Anforderung, zusammenhängende Ganzzahlwerte in einer Gruppe zusammenzufassen, wobei die Summe der Werte in jeder Gruppe nicht größer als 5 sein darf:

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

4. Streamable<T>

Eine Instanz vonStream kann nicht wiederverwendet werden. Aus diesem Grund istStreamable provides reusable streams by wrapping and exposing the same methods as the Stream:

Streamable s = Streamable.of("a", "b", "c", "d");
List collected1 = s.collect(Collectors.toList());
List collected2 = s.collect(Collectors.toList());
assertThat(collected1).hasSize(4);
assertThat(collected2).hasSize(4);

5. CollectorUtils

CollectorUtils ergänzt den StandardCollectors durch Hinzufügen mehrerer nützlicher Kollektormethoden.

5.1. maxBy() andminBy()

maxBy()finds the maximum value in a stream using supplied projection logic:

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

Im Gegensatz dazuminBy()finds the minimum value using the supplied projection logic.

5.2. unique()

Derunique()-Kollektor macht eine sehr einfache Sache:it returns the only value if a given stream has exactly 1 element:

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

Andernfalls löstunique() eine Ausnahme aus:

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

6. Fazit

In diesem Artikel haben wir erfahren, wie die Protonpack-Bibliothek die Java Stream-API erweitert, um die Verwendung zu vereinfachen. Es werden nützliche Methoden hinzugefügt, die wir normalerweise verwenden, die aber in der Standard-API fehlen.

Ab Java 9 sind einige der von Protonpack bereitgestellten Funktionen in der Standard-Stream-API verfügbar.

Wie üblich kann der Codeover on Github gefunden werden.