Detail the for each cycles and iterations in JAVA

  • 2020-05-12 02:33:42
  • OfStack

When studying collection in java, notice that collection level root interface Collection implements Iterable < T > Interface (in the java.lang package), which allows an object to be the target of an "foreach" statement. The only method in this interface is to return an iterator that iterates over a set of elements of type T.

1. Iterator Iterator

Interface: Iterator < T >


public interface Iterator<E>{
  boolean hasNext(); 
 E next(); 
 void remove();
 }

If you look at the Iterator interface, API, this is the iterator that iterates over collection. Iterators allow callers to take advantage of well-defined semantics to remove elements from collection that the iterator points to during an iteration.

Of particular note is the use of the iterator remove() method to remove the last element returned by the iterator (optional operation) from the iterator pointing to collection. This method can only be called once per call to next. If the iteration modifies the collection that the iterator points to in a way other than by calling this method (the remove method), the iterator's behavior is uncertain. Interface designers are designing Iterator < T > As you can see from the interface, if you call the remove() method in addition to the iterator to modify the collection pointed to by the iterator during iteration, there will be uncertain consequences. What happens depends on the implementation of the iterator. In view of the possible situations with uncertain consequences, I encountered one of them when learning ArrayList: an iterator throws an ConcurrentModificationException exception. The specific exception is shown in the following code:


import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class ItaratorTest {

  public static void main(String[] args) {
    Collection<String> list = new ArrayList<String>();
    list.add("Android");
    list.add("IOS");
    list.add("Windows Mobile");

    Iterator<String> iterator = list.iterator();
    while (iterator.hasNext()) {
      String lang = iterator.next();
      list.remove(lang);//will throw ConcurrentModificationException
    }
  }

}

This code throws an ConcurrentModificationException exception at runtime, because instead of removing elements from iterator's remove() method during the iterator run, we used remove() method from ArrayList to change the collection that the iterator points to. This violates the design principles of iterators, so an exception occurs.

The reported abnormal conditions are as follows:

Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
at java.util.ArrayList$Itr.next(ArrayList.java:831)
at Text.ItaratorTest.main(ItaratorTest.java:17)

2. for-each loop and iterator Iterator < T >

From Java5, there is for-each loop in Java, which can be used to loop through collection and array. The Foreach loop allows you to traverse collection without having to maintain the index in the traditional for loop or call the hasNext() method in the while loop when using iterator /ListIterator(one of the iterator implementations in ArrayList). The for-each loop simplifies the traversal of any Collection or array. But there are two caveats to using the foreach loop.

Objects that use the foreach loop must implement Iterable < T > interface

See the following example:


import java.util.ArrayList;

public class ForeachTest1 {

  public static void main(String args[]) {
    CustomCollection<String> myCollection = new CustomCollection<String>();
    myCollection.add("Java");
    myCollection.add("Scala");
    myCollection.add("Groovy");

    // What does this code will do, print language, throw exception or
    // compile time error
    for (String language : myCollection) {
      System.out.println(language);
    }
  }

  private class CustomCollection<T> {
    private ArrayList<T> bucket;

    public CustomCollection() {
      bucket = new ArrayList();
    }

    public int size() {
      return bucket.size();
    }

    public boolean isEmpty() {
      return bucket.isEmpty();
    }

    public boolean contains(T o) {
      return bucket.contains(o);
    }

    public boolean add(T e) {
      return bucket.add(e);
    }

    public boolean remove(T o) {
      return bucket.remove(o);
    }

  }
}

The above code will not compile because the CustomCollection class in the code does not implement Iterable < T > Interface, error reporting at compile time is as follows:

Exception in thread "main" java.lang.Error: Unresolved compilation problem:
Can only iterate over an array or an instance of java.lang.Iterable

at Text.ForeachTest1.main(ForeachTest1.java:15)

In fact, instead of waiting for the error to be detected at compile time, eclipse will display the error in the foreach loop after this code has been written: Can only iterate an or an instance of java.lang.Iterable

What you can confirm again from the example above is that the foreach loop applies only to those who have implemented Iterable < T > The object of the interface. Since all the built-in Collection classes implement the java.util.Collection interface and have inherited Iterable, you can simply have CustomCollection implement the Collection interface or inherit AbstractCollection to solve the above problem. The solution is as follows:


import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Iterator;

public class ForeachTest {
  public static void main(String args[]) {
    CustomCollection<String> myCollection = new CustomCollection<String>();
    myCollection.add("Java");
    myCollection.add("Scala");
    myCollection.add("Groovy");
    for (String language : myCollection) {
      System.out.println(language);
    }
  }

  private static class CustomCollection<T> extends AbstractCollection<T> {
    private ArrayList<T> bucket;

    public CustomCollection() {
      bucket = new ArrayList();
    }

    public int size() {
      return bucket.size();
    }

    public boolean isEmpty() {
      return bucket.isEmpty();
    }

    public boolean contains(Object o) {
      return bucket.contains(o);
    }

    public boolean add(T e) {
      return bucket.add(e);
    }

    public boolean remove(Object o) {
      return bucket.remove(o);
    }

    @Override
    public Iterator<T> iterator() {
      // TODO Auto-generated method stub
      return bucket.iterator();
    }
  }
}

2. The internal implementation of foreach loop is also realized by Iterator

In order to verify the fact that foreach loop USES Iterator as the internal implementation, we still use the example at the beginning of this article to verify:


public class ItaratorTest {

  public static void main(String[] args) {
    Collection<String> list = new ArrayList<String>();
    list.add("Android");
    list.add("IOS");
    list.add("Windows Mobile");

    // example1
    // Iterator<String> iterator = list.iterator();
    // while (iterator.hasNext()) {
    // String lang = iterator.next();
    // list.remove(lang);
    // }

    // example 2
    for (String language : list) {
      list.remove(language);
    }
  }

}

Exceptions reported when the program is running:

Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
at java.util.ArrayList$Itr.next(ArrayList.java:831)
at Text.ItaratorTest.main(ItaratorTest.java:22)

This exception indicates that for-each loop USES Iterator to traverse Collection, which also calls Iterator.next (), which checks for changes and throws ConcurrentModificationException.

Conclusion:

When traversing collection, if you want to modify collection during the traversal, you must do so through Iterator/listIterator, otherwise "uncertain consequences" may occur.
The foreach loop is implemented by iterator, and objects that use the foreach loop must implement the Iterable interface

Related articles: