The simplified code is below. I expected that return inside the iteration will result in jumping out of the method. Instead the code executes the next iteration and then the print after iterations within the method
public static void callIter() {
List<String> names = new ArrayList<>();
names.add("A");
names.add("B");
names.add("C");
ListIterator<String> nameIterator = names.listIterator();
nameIterator.forEachRemaining(name -> {
if (name.equals("B")) {
System.out.println("Found");
return;
}
System.out.println(name);
});
System.out.println("AfterIter");
}
The return
finishes execution of the code that is executed for each (remaining) element of the iterator. It does not stop execution of the iteration/forEachRemaining
. This matches the behaviour you are describing.
The Javadoc for forEachRemaining
states:
Performs the given action for each remaining element until all elements have been processed or the action throws an exception. Actions are performed in the order of iteration, if that order is specified. Exceptions thrown by the action are relayed to the caller.
In other words, if you don't want to abuse exceptions for this, you should not use forEachRemaining
if you intend to stop the iteration before processing the last element.
The forEach
methods, whether Iterable.forEach
, Iterator.forEachRemaining
or Stream.forEach
are intended to do what the name suggests, apply an action for each element, not just some.
Stopping the iteration is not forseen by this API, which can be recognized by looking at the functional signature of the Consumer
interface, which is (T) -> void
, not containg any possibility for the collection or iterator to notice a stop condition.
At this place, you should reconsider whether you are using the right tool for the job. Compare your approach with, e.g.
List<String> names = Arrays.asList("A", "B", "C");
int ix = names.indexOf("B");
(ix<0? names: names.subList(0, ix)).forEach(System.out::println);
if(ix>=0) System.out.println("Found");
System.out.println("AfterIter");
Of course, if the printing of the elements was only for debugging purposes, the actual operation simplifies to
List<String> names = Arrays.asList("A", "B", "C");
if(names.contains("B")) System.out.println("Found");
System.out.println("AfterIter");
If equality was only a placeholder for an arbitrary predicate, you may use
List<String> names = Arrays.asList("A", "B", "C");
if(names.stream().anyMatch(s -> s.equals("B"))) System.out.println("Found");
System.out.println("AfterIter");
which can be adapted to arbitrary conditions.
This can be expanded to
List<String> names = Arrays.asList("A", "B", "C");
Optional<String> match = names.stream()
.peek(System.out::println)
.filter(Predicate.isEqual("B"))
.findFirst();
if(match.isPresent()) System.out.println("Found");
// or
match.ifPresent(s -> System.out.println("Found "+s));
System.out.println("AfterIter");
showing, how you can debug the processing by printing the elements, an alternative way to express the equality predicate (still can be replaced by other predicates), and how to get the actual matching element for nontrivial predicates.
Generally, if you want to utilize the new APIs of Java 8, don’t try to write your logic in terms of forEach
. Rather, try to avoid forEach
, using more appropriate operations whenever possible.
forEachRemaining does not have option to break iteration. You can do search through stream operations, like this:
String name = names.stream().filter(name -> name.equals("B")).findFirst().orElse(null);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With