Forin statements in Java

Source: Internet
Author: User
Tags first string iterable

The principle of Forin

The Forin statement is a new feature of the JDK5 version, before which there are two ways to iterate over an array or collection: through the subscript and through the iterator. Let me give you an example:

@Testpublic void demo() {    String arr[] = { "abc", "def", "opq" };    for (int i = 0; i < arr.length; i++) {//通过下标遍历数组        System.out.println(arr[i]);    }    System.out.println("----------");    List<String> list = new ArrayList<String>();    list.add("abc");    list.add("def");    list.add("opq");    Iterator<String> iterator = list.iterator();    while (iterator.hasNext()) {//通过迭代器遍历集合        System.out.println(iterator.next());    }}

With JUnit for monomer testing, the output of the two methods is the same:


Demo () Run effect


JDK5 introduced the Forin statement to simplify the iterator traversal, which is still essentially an iterator traversal. The Forin statement is simple:

for(数据类型 对象名 : 数组或集合名){    ...}

The data type here is the data type in the array or collection, and then declares an object of that data type, which is used in place of each element in the array or collection (so the Forin statement is also called the foreach statement), and finally the manipulation of the object, which is the element in the array or collection.
Modify the above code and use the Forin statement to iterate over the array and the collection:

System.out.println("----------");for (String s1 : arr) {    System.out.println(s1);}System.out.println("----------");for (String s2 : list) {    System.out.println(s2);}

Using JUnit for monomer testing, the output is the same as before:


Demo () Run effect

It is important to note that traversing through Forin statements and iterating through iterators is completely equivalent. In addition, when programming with Eclipse, it alt is convenient to use + / for quick input to generate a For Loop statement or Forin statement for the subscript traversal.
Here is a question about array memory, add another paragraph in the code above:

System.out.println("----------");for (String s3 : arr) {    s3 = "rst";}System.out.println(arr[0]);

If you follow conventional thinking, the three elements in the array should all be modified rst so that the result of the final output should be all rst . However, this is not the case with JUnit for monomer testing:


Demo () Run effect

The result is obvious that the output is abc , def instead of opq three, and that the rst three elements in the array are not rst replaced. To explain the problem, start with the memory in Java, where the reference in the method is in heap space, and the object is instantiated in the stack space. Arrays are { "abc", "def", "opq" } references in methods, so stored in heap space, s3 and arr objects that are instantiated, should be stored in the stack space. In String arr[] = { "abc", "def", "opq" }; This code, = the function is to point the stack space to the arr array in the heap space, and the function of the Forin statement is that the value of the array elements in the heap space is assigned to the stack space once per loop s3 , and the values of these elements do not actually change. So the result of traversing and outputting all the elements of an array is exactly the same as before. Can help understand the problem:


Implementation of array memory Forin

If an object wants to traverse using the Forin statement, the object class must meet two conditions: Implement the Iterable interface and implement the Iterator method. ArrayListthe collection class is able to implement the Forin statement traversal because it satisfies both of these conditions:


Collection interface Inherits Iterable interface
Collection interface Implementation Iterator method


Because the ArrayList Collection class inherits the Abstractlist class, abstractlist class inheritance Abstractcollection class, Abstractcollection class implements Collection interface, so ArrayList The collection class implicitly implements the iterable interface and Iterator methods.
Now we're trying to write a phone class, and then let the phone class object implement the Forin statement traversal:

public class Phone implements Iterable<String> {//实现Iterable接口    String[] names = { "苹果", "三星", "华为", "小米", "魅族" };    public Iterator<String> iterator() {//实现Iterator方法同时自定义迭代器        Iterator<String> iterator = new MyIterator();        return iterator;    }    class MyIterator implements Iterator<String> {        int index = 0;         public boolean hasNext() {            if (index >= names.length) {                 return false;            }            return true;        }        public String next() {            String name = names[index];            index++;            return name;        }        public void remove() {        }    }}

Create a new method for testing:

@Testpublic void demo1(){    Phone phone = new Phone();//实例化Phone类对象    for (String s : phone) {//forin语句遍历Phone类对象phone        System.out.println(s);    }}

Tested with JUnit, the result is correct:


Demo1 () run result Forin Delete element

Create another method, this time making some changes to the elements of the collection, and then delete the string containing the characters in two ways a . The first is to iterate through the collection by subscript:

@Testpublic void demo2(){    List<String> list = new ArrayList<String>();    list.add("abc");    list.add("ade");    list.add("afg");    list.add("def");    list.add("opq");    for (int i = 0; i < list.size(); i++) {        String s = list.get(i);        if (s.contains("a")){            list.remove(s);        }    }    System.out.println(list);}

This code looks correct, but the output is wrong:


Demo2 () Run effect

This is because when the first string is deleted abc , the second string ade automatically becomes the first string, so the resulting string is not the same as the current one, and the string is not 1 deleted and ade afg ade the result is incorrect.
To prevent similar errors when deleting a collection element by subscript, the subscript should be reduced by one after each deletion of the element i-- . After correcting the code, test again and the result is correct:


Demo2 () Run effect

followed by the Forin statement traversal, it is very simple to think that the code should be:

for (String s : list) {    if(s.contains("a")){        list.remove(s);    }}System.out.println(list);

However, it backfired, the program error, throws an exception:


Program error

This exception is a concurrency modification exception. We will focus on the third line of error message, you can find that the class ArrayList Itr (iterator Class) next() method has an exception, view the declaration of the method, you will see the checkForComodification() method called, continue to view the declaration:


Checkforcomodification () method declaration


There are two parameters: modCount and, and expectedModCount if these two parameters are not equal, a concurrent modification exception is thrown. The expectedModCount parameter is the initialization length of the collection, and the modCount parameter is the current length of the collection. Back to the declaration of the class ArrayList in the class Itr , there is a code like this:


Set length initialization

That is, when the collection is initialized, expectedModCount it modCount is equal, but once the elements are added to or removed from the collection, the two will not equal and throw an exception.
To solve the problem of throwing an exception, you can use the Itr methods in the class to remove() see the declaration of the method first:


Remove () method declaration

There is a code that is critical: expectedModCount = modCount; . It is obvious that the calling remove() method can be set expectedModCount modCount to equal, so that the program can avoid throwing concurrent modification exceptions.
remove()Delete the elements of the collection using the method of the collection iterator:

Iterator<String> iterator = list.iterator();while (iterator.hasNext()) {    String s = iterator.next();    if (s.contains("a")) {        iterator.remove();    }}System.out.println(list);

By using JUnit to test the monomer, the result is naturally correct:


Demo2 () Run effect

If you only need to delete an element in the collection, such as deleting a string afg , you can delete it using the method of the collection remove() , but only if you want the statement to jump out of the loop after the deletion break :

for (String s : list) {    if (s.equals("afg")) {        list.remove(s);        break;    }}    System.out.println(list);

Demo2 () Run effect

The principle is also very simple, remember the previous introduction of the Forin statement is an iterator traversal it? breakjumping out of a loop with a statement makes the iterator unable next() to invoke the method, and does not throw concurrent modification exceptions.
There is also a way to throw an exception is determined by the nature of the collection itself, if the use of a collection that does not throw such exceptions will not solve the problem? The JDK5 version introduces the concept of a Copy-On-Write container, the CopyOnWrite idea of the mechanism is: when we add or delete elements to a container, not directly to the current container to add or delete, but first to the current container Copy , copy a new container, and then add or delete elements in the new container, After that, the reference to the original container is then pointed to the new container. There are currently CopyOnWriteArrayList and CopyOnWriteArraySet two implementation classes, so we can take CopyOnWriteArrayList classes:

List<String> list = new CopyOnWriteArrayList<String>();list.add("abc");list.add("ade");list.add("afg");list.add("def");list.add("opq");for (String s : list) {    if (s.contains("a")){        list.remove(s);    }}System.out.println(list);

Tested with JUnit, the result is correct:


Demo2 () Run effect

Forin statements in Java

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.