ホームページ  >  記事  >  类库下载  >  JAVAのfor-eachループと反復

JAVAのfor-eachループと反復

高洛峰
高洛峰オリジナル
2016-10-19 09:59:151768ブラウズ

1. イテレーター Iterator

インターフェース: Iterator

public interface Iterator<E>{

 boolean hasNext();

 E next();

 void remove();
 }

Iterator インターフェース API を見ると、これがコレクションを反復するためのイテレーターであることがわかります。イテレーターを使用すると、呼び出し元は、明確に定義されたセマンティクスを使用して、反復中にイテレーターが指すコレクションから要素を削除できます。

特に注目に値するのは、この反復子の Remove() メソッドの使用です。反復子によって返された最後の要素 (オプションの操作) を、反復子が指すコレクションから削除します。このメソッドは、next の呼び出しごとに 1 回だけ呼び出すことができます。このメソッド (remove メソッド) の呼び出し以外の方法で反復中にイテレーターが指すコレクションが変更された場合、イテレーターの動作は未定義です。インターフェイス設計者は、Iterator インターフェイスを設計するときに、反復中にイテレーターが指すコレクションを変更するためにイテレーター以外の Remove() メソッドが呼び出された場合、不確実な結果が生じることを指摘しました。具体的な結果は、反復子の特定の実装によって異なります。このような不確実な結果が発生する可能性のある状況に対応して、ArrayList を学習しているときにそのうちの 1 つ、つまり反復子が ConcurrentModificationException 例外をスローすることに遭遇しました。特定の例外は次のコードに示されています。

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
        }
    }

}

このコードは、実行中に ConcurrentModificationException 例外をスローします。これは、イテレータの実行中に要素を削除するためにイテレータの Remove() メソッドを使用せず、ArrayList の Remove() メソッドを使用するためです。イテレータが指すコレクションを変更します。これはイテレータの設計原則に違反するため、例外が発生します。
報告された例外は次のとおりです:

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 ループと反復子 Iterator

Java5 以降、Java には for-each ループがあり、コレクションと配列をループするために使用できます。 Foreach ループを使用すると、従来の for ループでインデックスを維持したり、イテレータ / ListIterator (ArrayList のイテレータ実装) を使用するときに while ループで hasNext() メソッドを呼び出すことなく、コレクションを反復処理できます。 for-each ループにより、コレクションまたは配列を走査するプロセスが簡素化されます。ただし、foreach ループを使用する場合は注意すべき点が 2 つあります。

foreach ループを使用するオブジェクトは Iterable インターフェイスを実装する必要があります

次の例を参照してください:

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

    }
}

これは、コード内の CustomCollection クラスが Iterable インターフェイスを実装していないためです。コンパイル中に報告される内容は次のとおりです:

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)

実際、Eclipse は、このコードを記述した後、コンパイルでエラーを見つけるまで待つ必要はありません。配列またはインスタンスのみを反復処理できます。 java.lang.Iterable

上記の例から再度確認できることは、foreach ループは Iterable インターフェースを実装するオブジェクトにのみ適用されるということです。すべての組み込み Collection クラスは java.util.Collection インターフェイスを実装し、Iterable を継承しているため、上記の問題を解決するには、単に CustomCollection に Collection インターフェイスを実装させるか、AbstractCollection を継承させるかを選択できます。解決策は次のとおりです:

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. foreach ループの内部実装も Iterator に依存します

foreach ループが内部実装として 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");

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

}

プログラムの実行時に報告される例外:

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)

この例外は、コレクションを走査するために for-each ループ内で Iterator が使用されていること、および変更をチェックする Iterator.next() を呼び出していることを示しています (の要素)、ConcurrentModificationException をスローします。

概要:

コレクションを走査するときに、走査中にコレクションを変更したい場合は、Iterator/listIterator を使用して変更する必要があります。そうしないと、「未確定の結果」が発生する可能性があります。

foreach ループはイテレータを通じて実装され、foreach ループを使用するオブジェクトは Iterable インターフェイスを実装する必要があります


声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。