Hoe te breken met Java Stream forEach

1. Overzicht

Als Java-ontwikkelaars schrijven we vaak code die zich herhaalt over een reeks elementen en een bewerking op elk element uitvoert. De Java 8-streams-bibliotheek en zijn voor elk methode stelt ons in staat om die code op een schone, declaratieve manier te schrijven.

Hoewel dit vergelijkbaar is met loops, we missen het equivalent van de breken verklaring om iteratie af te breken. Een stream kan erg lang zijn, of potentieel oneindig, en als we geen reden hebben om het verder te verwerken, zouden we ermee willen breken in plaats van op het laatste element te wachten.

In deze tutorial gaan we enkele mechanismen bekijken die laten we een simuleren breken verklaring over een Stream. Voor elk operatie.

2. Java 9's Stream.takeWhile ()

Laten we aannemen dat we een stroom hebben Draad items en we willen de elementen ervan verwerken zolang hun lengte oneven is.

Laten we de Java 9 proberen Stream.takeWhile methode:

Stream.of ("kat", "hond", "olifant", "vos", "konijn", "eend") .takeWhile (n -> n.length ()% 2! = 0) .forEach (System. out :: println);

Als we dit uitvoeren, krijgen we de uitvoer:

kat hond

Laten we dit vergelijken met de equivalente code in gewoon Java met behulp van een voor lus en een breken verklaring, om ons te helpen zien hoe het werkt:

List list = asList ("kat", "hond", "olifant", "vos", "konijn", "eend"); for (int i = 0; i <list.size (); i ++) {String item = list.get (i); if (item.length ()% 2 == 0) {break; } System.out.println (item); } 

Zoals we kunnen zien, is de nemenWhile methode stelt ons in staat om precies te bereiken wat we nodig hebben.

Maar wat als we Java 9 nog niet hebben geadopteerd? Hoe kunnen we iets soortgelijks bereiken met Java 8?

3. Een gewoonte Spliterator

Laten we een custom maken Spliterator dat zal werken als een decorateur voor een Stream.spliterator. We kunnen dit maken Spliterator voer de breken voor ons.

Eerst krijgen we de Spliterator van onze stroom, dan zullen we het versieren met onze CustomSpliterator en geef het Predikaat om de breken operatie. Ten slotte maken we een nieuwe stream van het CustomSpliterator:

openbare statische stroom takeWhile (stroomstroom, predikaatpredikaat) {CustomSpliterator customSpliterator = nieuwe CustomSpliterator (stream.spliterator (), predikaat); retour StreamSupport.stream (customSpliterator, false); }

Laten we eens kijken hoe we het CustomSpliterator:

openbare klasse CustomSpliterator breidt Spliterators.AbstractSpliterator {private Spliterator splitr; privé predikaat predikaat; private boolean isMatched = true; openbare CustomSpliterator (Spliterator splitr, Predicaat-predikaat) {super (splitr.estimateSize (), 0); this.splitr = splitr; this.predicate = predikaat; } @Override openbaar gesynchroniseerde boolean tryAdvance (Consumer consumer) {boolean hadNext = splitr.tryAdvance (elem -> {if (predicate.test (elem) && isMatched) {consumer.accept (elem);} else {isMatched = false;} }); return hadNext && isMatched; }}

Laten we dus eens kijken naar het probeer Advance methode. We kunnen hier zien dat de gewoonte Spliterator verwerkt de elementen van het versierde Spliterator. De verwerking wordt gedaan zolang ons predikaat overeenkomt en de initiële stroom nog steeds elementen bevat. Wanneer een van de voorwaarden wordt false, onze Spliterator"Pauzes" en de streaming-operatie eindigt.

Laten we onze nieuwe helper-methode testen:

@Test openbaar ongeldig whenCustomTakeWhileIsCalled_ThenCorrectItemsAreReturned () {Stream initialStream = Stream.of ("kat", "hond", "olifant", "vos", "konijn", "eend"); Lijstresultaat = CustomTakeWhile.takeWhile (initialStream, x -> x.length ()% 2! = 0) .collect (Collectors.toList ()); assertEquals (asList ("kat", "hond"), resultaat); }

Zoals we kunnen zien, stopte de stream nadat aan de voorwaarde was voldaan. Voor testdoeleinden hebben we de resultaten in een lijst verzameld, maar we hadden ook een voor elk call of een van de andere functies van Stroom.

4. Een gewoonte voor elk

Terwijl u een Stroom met de breken ingebed mechanisme kan nuttig zijn, het kan eenvoudiger zijn om te focussen op alleen het voor elk operatie.

Laten we de Stream.spliterator direct zonder decorateur:

openbare klasse CustomForEach {openbare statische klasse Breaker {privé boolean shouldBreak = false; public void stop () {shouldBreak = true; } boolean get () {return shouldBreak; }} openbare statische leegte forEach (Stream stream, BiConsumer consument) {Spliterator spliterator = stream.spliterator (); boolean hadNext = true; Brekerbreker = nieuwe Breker (); while (hadNext &&! breaker.get ()) {hadNext = spliterator.tryAdvance (elem -> {consumer.accept (elem, breaker);}); }}}

Zoals we kunnen zien, de nieuwe gewoonte voor elk methode roept een BiConsumer door onze code te voorzien van zowel het volgende element als een brekerobject dat het kan gebruiken om de stroom te stoppen.

Laten we dit uitproberen in een unit-test:

@Test openbare leegte whenCustomForEachIsCalled_ThenCorrectItemsAreReturned () {Stream initialStream = Stream.of ("kat", "hond", "olifant", "vos", "konijn", "eend"); Lijstresultaat = nieuwe ArrayList (); CustomForEach.forEach (initialStream, (elem, breaker) -> {if (elem.length ()% 2 == 0) {breaker.stop ();} else {result.add (elem);}}); assertEquals (asList ("kat", "hond"), resultaat); }

5. Conclusie

In dit artikel hebben we gekeken naar manieren om het equivalent van bellen te bieden breken op een stroom. We hebben gezien hoe Java 9's nemen lost het grootste deel van het probleem voor ons op en hoe we een versie daarvan voor Java 8 kunnen leveren.

Ten slotte hebben we gekeken naar een hulpprogramma-methode die ons het equivalent van een breken bewerking tijdens het itereren op een Stroom.

Zoals altijd is de voorbeeldcode te vinden op GitHub.


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