JAVAのfor-eachループと反復の詳細な説明

高洛峰
リリース: 2017-01-21 16:43:06
オリジナル
1341 人が閲覧しました

Java でコレクションを学習しているときに、コレクション レベルのルート インターフェイス Collection が Iterable インターフェイス (java.lang パッケージにある) を実装していることに気づきました。このインターフェイスを実装すると、オブジェクトが "foreach" のターゲットになることができます。これを実現する唯一の方法は、型 T の要素のセットを反復処理する反復子を返すことです。

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
    }
  }
 
}
ログイン後にコピー

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

報告された例外は次のとおりです:

スレッド「メイン」の例外 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 を実装していないため、上記のコードはコンパイルされません。 ; インターフェイスでは、コンパイル中に報告されるエラーは次のとおりです:

スレッド "main" java.lang.Error の例外: 未解決のコンパイルの問題:
での配列または java.lang.Iterable

のインスタンスのみを反復処理できます。 Text.ForeachTest1.main(ForeachTest1.java:15)

実際、このコードを記述した後、Eclipse はエラーを見つけるために foreach ループにエラーを表示します。配列または 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);
    }
  }
 
}
ログイン後にコピー

プログラムの実行中に例外が報告されました:

スレッド「メイン」の例外 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)

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

概要:

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

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

以上がこの記事の全内容です。皆さんの学習に役立つことを願っています。また、皆さんも PHP 中国語 Web サイトをサポートしていただければ幸いです。

JAVA の for-each ループと反復に関連する詳細な記事については、PHP 中国語 Web サイトに注目してください。


関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!