Ako prelomiť z Java Stream pre každý

1. Prehľad

Ako vývojári Java často píšeme kód, ktorý iteruje cez množinu prvkov a s každým vykoná operáciu. Knižnica streamov Java 8 a jej pre každý metóda nám umožňuje napísať tento kód čistým a deklaratívnym spôsobom.

Aj keď je to podobné ako slučky, chýba nám ekvivalent prestávka vyhlásenie o ukončení iterácie. Prúd môže byť veľmi dlhý alebo potenciálne nekonečný, a ak nemáme dôvod pokračovať v jeho spracovávaní, chceli by sme sa od neho odtrhnúť, a nie čakať na jeho posledný prvok.

V tomto tutoriále sa pozrieme na niektoré mechanizmy, ktoré dovoľte nám simulovať a prestávka vyhlásenie na a Stream.forEach prevádzka.

2. Java 9 Stream.takeWhile ()

Predpokladajme, že máme prúd String položiek a chceme ich prvky spracovať, pokiaľ sú ich dĺžky nepárne.

Vyskúšajme Java 9 Stream.takeWhile metóda:

Stream.of ("mačka", "pes", "slon", "líška", "králik", "kačica") .takeWhile (n -> n.length ()% 2! = 0). ForEach (systém. out :: println);

Ak to spustíme, dostaneme výstup:

mačka pes

Porovnajme to s ekvivalentným kódom v obyčajnej Jave pomocou a pre slučka a a prestávka vyhlásenie, ktoré nám pomôže zistiť, ako to funguje:

Zoznam list = asList ("mačka", "pes", "slon", "líška", "králik", "kačica"); for (int i = 0; i <list.size (); i ++) {String item = list.get (i); if (item.length ()% 2 == 0) {break; } System.out.println (položka); } 

Ako vidíme, takeWhile Táto metóda umožňuje dosiahnuť presne to, čo potrebujeme.

ale čo ak sme ešte Java 9 neprijali? Ako môžeme dosiahnuť podobnú vec pomocou Java 8?

3. Vlastné Splitterator

Vytvorme si zvyk Splitterator ktoré budú fungovať ako dekoratéri pre a Stream.spliterator. Môžeme to vyrobiť Splitterator vykonať prestávka pre nás.

Najprv dostaneme Splitterator z nášho streamu, potom ho ozdobíme naším CustomSpliterator a poskytnúť Predikát na kontrolu prestávka prevádzka. Nakoniec vytvoríme nový stream z CustomSpliterator:

public static Stream takeWhile (Stream stream, Predicate predicate) {CustomSpliterator customSpliterator = nový CustomSpliterator (stream.spliterator (), predikát); vrátiť StreamSupport.stream (customSpliterator, false); }

Pozrime sa, ako vytvoriť CustomSpliterator:

verejná trieda CustomSpliterator rozširuje Spliterators.AbstractSpliterator {súkromný Spliterator splitr; súkromný predikát predikát; private boolean isMatched = true; public CustomSpliterator (Spliterator splitr, Predicate predicate) {super (splitr.estimateSize (), 0); this.splitr = splitr; this.predicate = predicate; } @Override public synchronized boolean tryAdvance (Consumer consumer) {boolean hadNext = splitr.tryAdvance (elem -> {if (predicate.test (elem) && isMatched) {consumer.accept (elem);} else {isMatched = false;} }); návrat hadNext && isMatched; }}

Poďme sa teda pozrieť na tryAdvance metóda. Vidíme tu, že zvyk Splitterator spracováva prvky zdobených Splitterator. Spracovanie sa vykonáva, pokiaľ je náš predikát zhodný a počiatočný tok má stále prvky. Keď nastane ktorákoľvek z podmienok nepravdivé, náš Splitterator„Prestávky“ a streamovacia operácia sa končí.

Vyskúšajme našu novú pomocnú metódu:

@Test public void whenCustomTakeWhileIsCalled_ThenCorrectItemsAreReturned () {Stream initialStream = Stream.of ("mačka", "pes", "slon", "líška", "králik", "kačica"); Výsledok zoznamu = CustomTakeWhile.takeWhile (initialStream, x -> x.length ()% 2! = 0) .collect (Collectors.toList ()); assertEquals (asList ("mačka", "pes"), výsledok); }

Ako vidíme, prúd sa po splnení podmienky zastavil. Na účely testovania sme zhromaždili výsledky do zoznamu, ale mohli sme tiež použiť a pre každý volanie alebo niektorá z ďalších funkcií telefónu Prúd.

4. Vlastné pre každý

Pri poskytovaní a Prúd s prestávka vložený mechanizmus môže byť užitočný, môže byť jednoduchšie zamerať sa iba na pre každý prevádzka.

Použime Stream.spliterator priamo bez dekoratéra:

public class CustomForEach {public static class Breaker {private boolean shouldBreak = false; public void stop () {shouldBreak = true; } boolean get () {return shouldBreak; }} public static void forEach (Stream stream, BiConsumer consumer) {Spliterator spliterator = stream.spliterator (); boolean hadNext = true; Breaker breaker = nový Breaker (); while (hadNext &&! breaker.get ()) {hadNext = spliterator.tryAdvance (elem -> {consumer.accept (elem, breaker);}); }}}

Ako vidíme, nový zvyk pre každý metóda volá a BiConsumer poskytnutím nášho kódu ako nasledujúcemu prvku, tak aj prerušovaciemu objektu, ktorý môže použiť na zastavenie streamu.

Vyskúšajme to v teste jednotky:

@Test public void whenCustomForEachIsCalled_ThenCorrectItemsAreReturned () {Stream initialStream = Stream.of ("mačka", "pes", "slon", "líška", "králik", "kačica"); Výsledok zoznamu = nový ArrayList (); CustomForEach.forEach (initialStream, (elem, breaker) -> {if (elem.length ()% 2 == 0) {breaker.stop ();} else {result.add (elem);}}); assertEquals (asList ("mačka", "pes"), výsledok); }

5. Záver

V tomto článku sme sa zaoberali spôsobmi, ako poskytnúť ekvivalent volania prestávka na potoku. Videli sme, ako funguje Java 9 takeWhile rieši väčšinu problémov pre nás a ako poskytnúť ich verziu pre Java 8.

Nakoniec sme sa pozreli na úžitkovú metódu, ktorá nám môže poskytnúť ekvivalent a prestávka operácia pri iterácii na a Prúd.

Ako vždy, ukážkový kód nájdete na GitHub.


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