Kako se odvojiti od Java Streama forEach

1. Pregled

Kao programeri Java, često pišemo kod koji se ponavlja kroz skup elemenata i izvodi operaciju nad svakim. Java 8 struji knjižnicu i njezinu za svakoga metoda dopuštaju nam da taj kod napišemo na čist, deklarativni način.

Iako je ovo slično petljama, nedostaje nam ekvivalent pauza izjava za prekid ponavljanja. Potok može biti vrlo dugačak ili potencijalno beskonačan, i ako nemamo razloga da je nastavimo obrađivati, htjeli bismo se odvojiti od nje, umjesto da čekamo njezin zadnji element.

U ovom uputstvu ćemo pogledati neke mehanizme koji dopustite nam da simuliramo a pauza izjava o a Stream.forEach operacija.

2. Java 9 Stream.takeWhile ()

Pretpostavimo da imamo tok Niz predmeta i želimo obraditi njegove elemente sve dok su njihove duljine neparne.

Pokušajmo s Java 9 Stream.takeWhile metoda:

Stream.of ("mačka", "pas", "slon", "lisica", "zec", "patka") .takeWhile (n -> n.length ()% 2! = 0) .forEach (System. van :: println);

Ako ovo pokrenemo, dobit ćemo izlaz:

mačka pas

Usporedimo ovo s ekvivalentnim kodom u običnoj Javi pomoću a za petlja i a pauza izjava, koja će nam pomoći da vidimo kako to radi:

Lista popisa = asList ("mačka", "pas", "slon", "lisica", "zec", "patka"); for (int i = 0; i <list.size (); i ++) {Stavka niza = list.get (i); if (item.length ()% 2 == 0) {break; } System.out.println (stavka); } 

Kao što vidimo, takeWhile metoda omogućuje nam da postignemo točno ono što nam treba.

Ali što ako još nismo usvojili Javu 9? Kako možemo postići sličnu stvar pomoću Jave 8?

3. Običaj Spliterator

Stvorimo običaj Spliterator koji će raditi kao dekorator za a Stream.spliterator. Možemo ovo napraviti Spliterator izvesti pauza za nas.

Prvo ćemo dobiti Spliterator iz našeg potoka, onda ćemo ga ukrasiti našim CustomSpliterator i pružiti Predikat kontrolirati pauza operacija. Napokon ćemo stvoriti novi stream iz CustomSpliterator:

javni statični Stream takeWhile (Stream stream, predikat predikata) {CustomSpliterator customSpliterator = novi CustomSpliterator (stream.spliterator (), predikat); vratiti StreamSupport.stream (customSpliterator, false); }

Pogledajmo kako stvoriti CustomSpliterator:

javna klasa CustomSpliterator proširuje Spliterators.AbstractSpliterator {privatni spliter Spliterator; privatni predikatni predikat; privatno boolean isMatched = true; javni CustomSpliterator (Spliterator spliter, predikat predikat) {super (splitr.estimateSize (), 0); this.splitr = spliter; this.predicate = predikat; } @Override javni sinkronizirani logički testAdvance (potrošački potrošač) {boolean hadNext = splitr.tryAdvance (elem -> {if (predikat.test (elem) && isMatched) {consumer.accept (elem);} else {isMatched = false;} }); return hadNext && isMatched; }}

Pa, pogledajmo tryAdvance metoda. Ovdje možemo vidjeti da je običaj Spliterator obrađuje elemente ukrašenog Spliterator. Obrada se vrši sve dok se podudara naš predikat i početni tok još uvijek ima elemente. Kad postane bilo koji od uvjeta lažno, naš Spliterator"Pauze" a operacija strujanja završava.

Isprobajmo našu novu pomoćnu metodu:

@Test public void whenCustomTakeWhileIsCalled_ThenCorrectItemsAreReturned () {Stream InitialStream = Stream.of ("mačka", "pas", "slon", "lisica", "zec", "patka"); Rezultat popisa = CustomTakeWhile.takeWhile (InitialStream, x -> x.length ()% 2! = 0) .collect (Collectors.toList ()); assertEquals (asList ("mačka", "pas"), rezultat); }

Kao što vidimo, potok se zaustavio nakon ispunjenja uvjeta. Za potrebe testiranja prikupili smo rezultate na popis, ali mogli smo koristiti i za svakoga poziv ili bilo koju drugu funkciju Stream.

4. Običaj za svakoga

Tijekom pružanja a Stream s pauza ugrađeni mehanizam može biti koristan, možda je jednostavnije usredotočiti se samo na za svakoga operacija.

Iskoristimo Stream.spliterator izravno bez dekoratera:

javna klasa CustomForEach {javna statička klasa Breaker {private boolean shouldBreak = false; javna void stop () {shouldBreak = true; } boolean get () {return shouldBreak; }} javna statička praznina forEach (stream stream, potrošač BiConsumer) {Spliterator spliterator = stream.spliterator (); boolean hadNext = true; Prekidač = novi Prekidač (); while (hadNext &&! breaker.get ()) {hadNext = spliterator.tryAdvance (elem -> {consumer.accept (elem, breaker);}); }}}

Kao što vidimo, novi običaj za svakoga metoda poziva a BiConsumer pružajući našem kodu i sljedeći element i objekt prekidača koji može koristiti za zaustavljanje struje.

Isprobajmo ovo u jediničnom testu:

@Test public void whenCustomForEachIsCalled_ThenCorrectItemsAreReturned () {Stream InitialStream = Stream.of ("mačka", "pas", "slon", "lisica", "zec", "patka"); Rezultat popisa = novi ArrayList (); CustomForEach.forEach (InitiStream, (elem, breaker) -> {if (elem.length ()% 2 == 0) {breaker.stop ();} else {result.add (elem);}}); assertEquals (asList ("mačka", "pas"), rezultat); }

5. Zaključak

U ovom smo članku pogledali načine kako pružiti ekvivalent pozivanja pauza na potoku. Vidjeli smo kako Java 9 takeWhile rješava većinu problema za nas i kako pružiti njegovu verziju za Javu 8.

Napokon, pogledali smo korisnu metodu koja nam može pružiti ekvivalent a pauza operacija tijekom ponavljanja a Stream.

Kao i uvijek, primjer koda možete pronaći na GitHubu.


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