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.