In Java, the for-each loop simplifies the traversal process of any Collection or array, but not every Java programmer knows some of the details of the for-each loop that will be described in this article. Unlike other terms released in Java 5: alias-released generics, auto-encapsulation and variadic parameters, Java developers use for-each loops more frequently than any other feature, but when asked how advanced for-each loops work, Or what are the basic requirements when using a Collection in a for-each loop, not everyone can answer it.
This tutorial and example aims to fill that gap by delving into a few interesting puzzles in for-each loops. Okay, let’s not go into details, let’s take a look at our first problem with the Java5 for-each loop.
Advanced Loop Question 1
Consider the following code that traverses a user-defined aggregator or collection class. What will this code print, whether it throws an exception or a compiler error:
Copy the code code as follows:
package test;
/**
* Java Class to show how for-each loop works in Java
*/
public class ForEachTest {
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);
}
}
}
Below is our CustomCollection class, which is a generic class that, like any other Collection class, relies on an ArrayList and provides methods for adding and removing items from the Collection.
Copy the code code as follows:
package test;
public 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);
}
}
Answer:
The above code will not compile because our CustomCollection class does not implement the java.lang.Iterable interface. The compile-time error is as follows:
Copy the code code as follows:
Exception in thread "main" java.lang.RuntimeException: Uncompilable source code - for-each not applicable to expression type
required: array or java.lang.Iterable
found: test.CustomCollection
at test.ForEachTest.main(ForEachTest.java:24)
An interesting fact to learn from this is that the for-each loop only applies to Java array and Collection classes that implement the Iterable interface, and since all built-in Collection classes implement the java.util.Collection interface and have inherited Iterable, this A detail that is often overlooked can be seen in the type declaration of the Collection interface "public interface Collection extends Iterable". So in order to solve the above problem, you can choose to simply let CustomCollection implement Collection interface or inherit AbstractCollection, which is the default universal implementation and shows how to use abstract classes and interfaces at the same time for better flexibility. Now let's look at the second puzzle of the for-each loop:
The second difficulty with Java for-each loops:
The following code example will throw a ConcurrentModificationException. Here we use standard iterator and for-each loop to iterate through the ArrayList and then delete the elements. You need to find out which piece of code will throw ConcurrentModificationException and why? Note that the answer could be both, neither, or one or the other.
Copy the code code as follows:
package test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
* Java class to demonstrate inner working of for-each loop in Java
* @author Javin Paul
**/
public class ForEachTest2 {
public static void main(String args[]){
Collection<String> list = new ArrayList<String>();
list.add("Android");
list.add("iPhone");
list.add("Windows Mobile");
// Which Code will throw ConcurrentModificationException, both,
// none or one of them
// example 1
Iterator<String> itr = list.iterator();
while(itr.hasNext()){
String lang = itr.next();
list.remove(lang);
}
//example 2
for(String language: list){
list.remove(language);
}
}
}
About 70% of Java developers will say that the first code block will throw a ConcurrentModificationException exception because we do not use iterator's remove method to delete elements, but use ArrayList's remove() method. However, not many Java developers would say that the same problem occurs with for-each loop as we are not using iterator here. In fact, the second code snippet also throws a ConcurrentModificationException, which becomes obvious after solving the first confusion. Since the for-each loop uses an Iterator internally to traverse the Collection, it also calls Iterator.next(), which checks for changes (of elements) and throws a ConcurrentModificationException. You can see this from the output below, which you get when you run the second snippet after commenting out the first snippet.
Copy the code code as follows:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
at test.ForEachTest2.main(ForEachTest2.java:34)
That’s all about the Java5 for-each loop. We've seen a lot of problems that Java programmers have when writing code to iterate over a Collection class, especially when iterating over a collection while deleting elements at the same time. Remember to always use the Iterator's remove method when removing objects from any Collection (such as a Map, Set, or List). Also remember that the for-each loop is just syntactic sugar (syntactic sugar) on top of the standard usage of the standard Iterator code. sugar) only.
Translator's Note: Syntactic sugar, also translated as sugar-coated grammar, is a term invented by British computer scientist Peter J. Landin, which refers to a certain kind of grammar added to computer languages. The syntax has no impact on the functionality of the language, but makes it easier for programmers to use. Generally speaking, using syntax sugar can increase the readability of the program, thereby reducing the chance of program code errors.