Sådan bryder du fra Java Stream for hver

1. Oversigt

Som Java-udviklere skriver vi ofte kode, der gentager sig over et sæt elementer og udfører en operation på hver enkelt. Java 8 streamer biblioteket og dets for hver metode tillader os at skrive denne kode på en ren, erklærende måde.

Mens dette ligner sløjfer, vi mangler det svarende til pause erklæring om at afbryde iteration. En stream kan være meget lang eller potentielt uendelig, og hvis vi ikke har nogen grund til at fortsætte med at behandle det, vil vi bryde fra det snarere end at vente på det sidste element.

I denne vejledning skal vi se på nogle mekanismer, der tillad os at simulere en pause erklæring om en Stream.forEach operation.

2. Java 9'er Stream.takeWhile ()

Lad os antage, at vi har en strøm af Snor emner, og vi vil behandle dets elementer, så længe deres længder er ulige.

Lad os prøve Java 9 Stream.takeWhile metode:

Stream.of ("kat", "hund", "elefant", "ræv", "kanin", "and"). TakeWhile (n -> n.length ()% 2! = 0) .forEach (System. ud :: println);

Hvis vi kører dette, får vi output:

kat hund

Lad os sammenligne dette med den tilsvarende kode i almindelig Java ved hjælp af en til løkke og en pause erklæring for at hjælpe os med at se, hvordan det fungerer:

Liste liste = asList ("kat", "hund", "elefant", "ræv", "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 tagMens metode giver os mulighed for at opnå præcis, hvad vi har brug for.

Men hvad hvis vi ikke har vedtaget Java 9 endnu? Hvordan kan vi opnå en lignende ting ved hjælp af Java 8?

3. En brugerdefineret Spliterator

Lad os oprette en brugerdefineret Spliterator der fungerer som dekoratør for en Stream.spliterator. Vi kan klare det Spliterator udføre pause for os.

Først får vi Spliterator fra vores strøm, så dekorerer vi det med vores CustomSpliterator og give Prædikat at kontrollere pause operation. Endelig opretter vi en ny stream fra CustomSpliterator:

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

Lad os se på, hvordan man opretter CustomSpliterator:

offentlig klasse CustomSpliterator udvider Spliterators.AbstractSpliterator {privat Spliterator splitr; privat Predikatpredikat; privat boolsk isMatched = sand; offentlig CustomSpliterator (Spliterator splitr, Predicate predicate) {super (splitr.estimateSize (), 0); this.splitr = splitr; this.predicate = predicate; } @ Override offentlig synkroniseret boolsk tryAdvance (forbrugerforbruger) {boolean hadNext = splitr.tryAdvance (elem -> {if (predicate.test (elem) && isMatched) {consumer.accept (elem);} else {isMatched = false;} }); returnere havdeNæste && erMatchet; }}

Så lad os se på prøv Avance metode. Vi kan se her, at skik Spliterator behandler elementerne i det dekorerede Spliterator. Behandlingen udføres, så længe vores prædikat matches, og den oprindelige strøm stadig har elementer. Når en af ​​betingelserne bliver falsk, vores Spliterator"pauser" og streamingoperationen slutter.

Lad os teste vores nye hjælpermetode:

@Test offentligt ugyldigt nårCustomTakeWhileIsCalled_ThenCorrectItemsAreReturned () {Stream initialStream = Stream.of ("kat", "hund", "elefant", "ræv", "kanin", "and"); Listeresultat = CustomTakeWhile.takeWhile (initialStream, x -> x.length ()% 2! = 0) .collect (Collectors.toList ()); assertEquals (resultat (asList ("cat", "dog")); }

Som vi kan se, stoppede strømmen, efter at betingelsen var opfyldt. Til testformål har vi samlet resultaterne på en liste, men vi kunne også have brugt en for hver opkald eller en af ​​de andre funktioner i Strøm.

4. En brugerdefineret for hver

Mens du leverer en Strøm med pause integreret mekanisme kan være nyttigt, kan det være enklere at fokusere på netop for hver operation.

Lad os bruge Stream.spliterator direkte uden dekoratør:

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

Som vi kan se, den nye brugerdefinerede for hver metode kalder en BiConsumer giver vores kode både det næste element og et breaker-objekt, det kan bruge til at stoppe strømmen.

Lad os prøve dette i en enhedstest:

@Test offentligt ugyldigt nårCustomForEachIsCalled_ThenCorrectItemsAreReturned () {Stream initialStream = Stream.of ("kat", "hund", "elefant", "ræv", "kanin", "and"); Listeresultat = ny ArrayList (); CustomForEach.forEach (initialStream, (elem, breaker) -> {if (elem.length ()% 2 == 0) {breaker.stop ();} ellers {result.add (elem);}}); assertEquals (resultat (asList ("kat", "hund")); }

5. Konklusion

I denne artikel har vi kigget på måder, hvorpå man kan svare til opkald pause på en strøm. Vi så, hvordan Java 9'er tagMens løser det meste af problemet for os, og hvordan man leverer en version af det til Java 8.

Endelig kiggede vi på en hjælpemetode, der kan give os ækvivalenten med en pause betjening, mens det gentages på en Strøm.

Som altid kan eksempelkoden findes på GitHub.