Slik bryter du fra Java Stream for hver

1. Oversikt

Som Java-utviklere skriver vi ofte kode som gjentas over et sett med elementer og utfører en operasjon på hver enkelt. Java 8 streamer biblioteket og dets for hver metoden tillater oss å skrive den koden på en ren, erklærende måte.

Selv om dette ligner på sløyfer, vi mangler ekvivalenten til gå i stykker uttalelse for å avbryte iterasjon. En strøm kan være veldig lang, eller potensielt uendelig, og hvis vi ikke har noen grunn til å fortsette behandlingen, vil vi bryte fra det, i stedet for å vente på det siste elementet.

I denne opplæringen skal vi se på noen mekanismer som la oss simulere a gå i stykker uttalelse om en Stream.forEach operasjon.

2. Java 9-er Stream.takeWhile ()

La oss anta at vi har en strøm av String elementer, og vi vil behandle elementene så lenge lengdene er merkelige.

La oss prøve Java 9 Stream.takeWhile metode:

Stream.of ("katt", "hund", "elefant", "rev", "kanin", "and"). TaWhile (n -> n.length ()% 2! = 0) .forEach (System. ut :: println);

Hvis vi kjører dette, får vi utdataene:

katt hund

La oss sammenligne dette med tilsvarende kode i vanlig Java ved hjelp av a til løkke og en gå i stykker uttalelse, for å hjelpe oss å se hvordan det fungerer:

Listeliste = asList ("katt", "hund", "elefant", "rev", "kanin", "and"); for (int i = 0; i <list.size (); i ++) {String item = list.get (i); if (item.length ()% 2 == 0) {pause; } System.out.println (element); } 

Som vi kan se, er taMens metoden lar oss oppnå akkurat det vi trenger.

Men hva om vi ikke har tatt i bruk Java 9 ennå? Hvordan kan vi oppnå en lignende ting ved hjelp av Java 8?

3. En egendefinert Spliterator

La oss lage en tilpasset Spliterator som vil fungere som dekoratør for en Stream.spliterator. Vi kan lage dette Spliterator utføre gå i stykker for oss.

Først får vi Spliterator fra strømmen vår, så dekorerer vi den med vår CustomSpliterator og gi Predikere for å kontrollere gå i stykker operasjon. Til slutt oppretter vi en ny strøm fra CustomSpliterator:

public static Stream takeWhile (Stream stream, Predicate predicate) {CustomSpliterator customSpliterator = new CustomSpliterator (stream.spliterator (), predicate); returner StreamSupport.stream (customSpliterator, false); }

La oss se på hvordan du lager CustomSpliterator:

offentlig klasse CustomSpliterator utvider Spliterators.AbstractSpliterator {private Spliterator splitr; privat Predikatpredikat; private boolske isMatched = true; offentlig CustomSpliterator (Spliterator splitr, Predicate predicate) {super (splitr.estimateSize (), 0); this.splitr = splitr; this.predicate = predikat; } @ Overstyr offentlig synkronisert boolsk tryAdvance (forbrukerforbruker) {boolsk hadNext = splitr.tryAdvance (elem -> {if (predicate.test (elem) && isMatched) {consumer.accept (elem);} else {isMatched = false;} }); return hadNext && isMatched; }}

Så, la oss ta en titt på prøv avansert metode. Vi kan se her at skikken Spliterator behandler elementene i det dekorerte Spliterator. Behandlingen gjøres så lenge predikatet vårt blir matchet og den første strømmen fortsatt har elementer. Når en av forholdene blir falsk, våre Spliterator"pauser" og streaming-operasjonen avsluttes.

La oss teste vår nye hjelpermetode:

@Test offentlig ugyldig nårCustomTakeWhileIsCalled_ThenCorrectItemsAreReturned () {Stream initialStream = Stream.of ("cat", "dog", "elephant", "fox", "rabbit", "duck"); Listeresultat = CustomTakeWhile.takeWhile (initialStream, x -> x.length ()% 2! = 0) .collect (Collectors.toList ()); assertEquals (asList ("cat", "dog"), result); }

Som vi ser, stoppet strømmen etter at vilkåret var oppfylt. For testformål har vi samlet resultatene i en liste, men vi kunne også ha brukt en for hver samtale eller noen av de andre funksjonene til Strøm.

4. En egendefinert for hver

Mens du gir en Strøm med gå i stykker innebygd mekanisme kan være nyttig, kan det være enklere å fokusere på bare for hver operasjon.

La oss bruke Stream.spliterator direkte uten dekoratør:

public class CustomForEach {public static class Breaker {private boolean shouldBreak = false; public void stop () {shouldBreak = true; } boolsk get () {return shouldBreak; }} offentlig statisk tomrom forEach (Stream stream, BiConsumer consumer) {Spliterator spliterator = stream.spliterator (); boolsk hadNext = true; Bryterbryter = ny Bryter (); mens (hadNext &&! breaker.get ()) {hadNext = spliterator.tryAdvance (elem -> {consumer.accept (elem, breaker);}); }}}

Som vi kan se, den nye skikken for hver metoden kaller en BiConsumer gir koden vår både neste element og et bryterobjekt det kan bruke til å stoppe strømmen.

La oss prøve dette i en enhetstest:

@Test offentlig ugyldig nårCustomForEachIsCalled_ThenCorrectItemsAreReturned () {Stream initialStream = Stream.of ("cat", "dog", "elephant", "fox", "rabbit", "duck"); Listeresultat = ny ArrayList (); CustomForEach.forEach (initialStream, (elem, breaker) -> {if (elem.length ()% 2 == 0) {breaker.stop ();} else {result.add (elem);}}); assertEquals (asList ("cat", "dog"), result); }

5. Konklusjon

I denne artikkelen så vi på måter å gi tilsvarende til å ringe gå i stykker på en strøm. Vi så hvordan Java 9 er taMens løser det meste av problemet for oss og hvordan du gir en versjon av det for Java 8.

Til slutt så vi på en verktøymetode som kan gi oss tilsvarende a gå i stykker operasjon mens det gjentas på en Strøm.

Som alltid kan eksempelkoden bli funnet på GitHub.


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