简体   繁体   中英

How to jump out of the method inside Java 8 Iterator.forEachRemaining loop?

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, eg

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);

As mentioned by others, Iterator.forEachRemaining does not have an easy break since it is intended to run through eachRemaining . A way that I got around this issue was this style:

Iterator<Object> objects = ...;
    while (objects.hasNext() && exitCondition) {
      // Do what you would've inside of the for each
    }

我的一位天才同事建议我在这种情况下在循环中抛出RuntimeException并在forEachRemainin循环之外捕获它

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM