Explain Codes LogoExplain Codes Logo

Move to next item using Java 8 foreach loop in stream

java
lambdas
streaming
iterators
Nikita BarsukovbyNikita Barsukov·Feb 28, 2025
TLDR

Utilize an Iterator to manually traverse elements in a Java Stream. forEach doesn't provide navigation controls like next(). Here is a slice of the action:

Iterator<YourType> it = myStream.iterator(); while(it.hasNext()) { YourType item = it.next(); // Do your thing with item }

In essence, you've got iteration authority over stream elements, bobbing and weaving around forEach's limitations. Remember, Streams are game for individual element treatment - for detailed iteration control, Iterator is your knight in shining armor.

Lambdas - not just a Greek letter

In a forEach loop, a return in a lambda doesn't cut the loop short - it merely stops the current item's operation. With the lambdas acting independently, they're sort of businesslike "methods" focused on one item at a time.

list.stream().forEach(item -> { if (item.isSkipWorthy()) { // Item asked for a break! return; // Temporary bailout, not a total eclipse of the loop } // Continue juicing the items unless a sour one shows up });

The return here is playing the role of a continue in a regular for or while loop, but confined to the item-specific lambda block.

Filtering: The good, the bad and the ugly

The filter method within a stream pipeline is the continue's stunt double with a reverse logic twist.

list.stream().filter(item -> !item.isSkipWorthy()).forEach(item -> { // Unpack only the items worth their salt });

By applying reverse-psychology (!condition) on your condition, the stream focuses its powers on elements that passed the secret test.

Shine a light on your code

Shaping lambdas as explicit methods can turn on the light for extra clarity and detailed control.

list.stream().forEach(this::itemProcessor); // Method reference; magic trick of the day private void itemProcessor(YourType item) { if (item.needsEarlyExit()) { return; // Only a short recess, not quiting the day job } // Other hard-working code }

This style clearly says return is the bell signifying a tea break, not a pack-up-your-desk happening.

The all-seeing takeWhile

Java 9 gifted us takeWhile to stop processing the moment it smells a condition.

list.stream().takeWhile(item -> !item.stopOmeterReadingHigh()).forEach(this::itemProcessor);

Now, you have control over the brakes until the red-light condition (item.stopOmeterReadingHigh()) turns green.

Streaming Nostalgia? Go Classic!

When Streams become overbearing, embrace the classic loop that comes with a pre-filtered list by your side:

List<YourType> VIPsOnly = list.stream() .filter(item -> !item.isSkipWorthy()) .collect(Collectors.toList()); for (YourType item : VIPsOnly) { // Item-specific magic, extra control over the loop }

The classic for-each loop with its pre-filtered guest list gives you back the realm of iteration. So, break, continue or even mull over changing the loop index while sipping coffee.

Tinkering with a custom Iterator

Considering puzzle-solving iteration requirements? Lay your hands on a custom iterator:

StreamEx.of(list).forEachIndexed((index, item) -> { if (itsASkipDay(item, index)) { return; // Plays 'continue', ready for the next roll of the dice } // Dives deep into the pool of items });

The StreamEx library or similar powerhouses enable you to flex your muscles over the iteration process.

When to wear the stream blinders

Gauge your circumstances:

  1. Nested loop scenarios: Good old-school loops could work better.
  2. Stateful business: Multiple states or conditions? Streams could jam your signals.
  3. Fancy for break or continue: Missing homely looping devices? Go traditional.

Streaming Caveats and Snap-Ons

Beware streams:

  • Parallel streams: Order of execution is not the forEach's jam.
  • Stateful Lambdas: Mind the unpredictable results!

Consider the Alternatives:

  • flatMap, map, reduce Methods or the tried and trusted StreamEx library for advanced looping paths.