Java学習ノート(38)-Java集合10のIterator

5365 ワード

反復は私たちがJavaをやっている人にとって決してよく知られていません.JDKが提供する反復インタフェースを用いてJava集合の反復を行うことが多い.
Iterator iterator = list.iterator();        
while(iterator.hasNext()){
            String string = iterator.next();   //do something
        }

反復は実際には単純に遍歴として理解でき,各種容器内のすべてのオブジェクトを標準化する方法クラスであり,典型的な設計モデルである.Iteratorモードは、集合クラスを巡回するための標準的なアクセス方法です.アクセスロジックを異なるタイプの集合クラスから抽象化し、クライアントに集合の内部構造を露出させることを避けることができます.反復器がない場合、私たちはこのように処理します.次のようになります.
配列については、下付き文字を使用して処理します.
int[] arrays = new int[10];   
for(int i = 0 ; i < arrays.length ; i++)
{       
    int a = arrays[i];       //do something
}

ArrayListでは、次のように処理されます.
List<String> list = new ArrayList<String>();   
for(int i = 0 ; i < list.size() ;  i++){
      String string = list.get(i);      //do something
   }

この2つの方法では,集合の内部構造が常に事前に知られており,アクセスコードと集合自体が緊密に結合されており,アクセスロジックを集合クラスとクライアントコードから分離することはできない.同時に、各セットはループメソッドに対応し、クライアントコードは多重化できません.実際の応用では,上記の2つの集合をどのように統合する必要があるかはかなり面倒である.だから、以上の問題を解決するために、Iteratorモードが登場し、いつも同じ論理で集合を遍歴しています.クライアント自身が集合の内部構造を維持する必要がなく、すべての内部状態がIteratorによって維持される.クライアントは集合クラスと直接関係を持たず、常にIteratorを制御し、「前へ」、「後ろへ」、「現在の要素を取る」コマンドを送信し、間接的に集合全体を巡回することができます.
上はただIteratorモードについて簡単に説明しただけで、JavaのIteratorインタフェースを見て、彼がどのように実現したかを見てみましょう.
一、java.util.Iterator
JavaではIteratorはインタフェースであり、JDKではcollectionを反復する反復器として定義されている基本ルールのみを提供しています.反復器はJava Collections FrameworkのEnumerationに取って代わった.反復器と列挙には2つの違いがあります.
1、反復器は、反復中に反復器が指すcollectionから要素を除去するために、呼び出し者が定義された良好な意味を利用することを可能にする.
2、方法名が改善された.
インタフェースの定義は次のとおりです.
public interface Iterator {
  boolean hasNext();
  Object next();
  void remove();
}

次のようになります.
Object next():反復器が越えたばかりの要素の参照を返します.戻り値はObjectです.必要なタイプに強制的に変換する必要があります.
boolean hasNext():コンテナ内にアクセス可能な要素があるかどうかを判断する
void remove():反復器が越えたばかりの要素を削除
我々にとって,next(),hasNext()の2つの方法を用いるだけで反復が完了するのが一般的である.次のようになります.
for(Iterator it = c.iterator(); it.hasNext(); ) {
  Object o = it.next();
   //do something
   }

前述したIteratorの大きな利点は,集合の内部結果を知る必要がなく,集合の内部構造,状態はIteratorによって維持され,統一的な方法hasNext(),next()によって次の要素を判断し,取得することであり,具体的な内部実現には関心を持たなくてもよいことである.しかし、合格したプログラマーとして、Iteratorの実現を明らかにする必要があります.以下、ArrayListのソースコードについて分析する.
二、各集合のIteratorの実現
以下にArrayListのIterator実装について分析するが,実際にArrayList,Hashset,TreeSetのデータ構造,内部実装を理解すれば,彼らがどのようにIteratorを実装するかについても成竹する.ArrayListの内部実装は配列を採用しているので,対応する位置のインデックスを記録するだけでよいので,その方法の実装は比較的簡単である.
2.1、ArrayListのIterator実現
ArrayList内部ではまず、以下のようにIteratorインタフェースを実装する内部クラスItrを定義する.
private class Itr implements Iterator<E> {    //do something}

ArrayListのiterator()メソッドは次のように実装されます.
public Iterator<E> iterator() {        
         return new Itr();
    }

したがって、ArrayList.iterator()メソッドを使用して返されるのはItr()内部クラスであるため、Itr()内部クラスの実装に注目する必要があります.
Itr内部には、cursor、lastRet、expectedModCountの3つのint型変数が定義されています.ここでcursorは次の要素のインデックス位置を表し、lastRetは前の要素のインデックス位置を表す
int cursor;             
int lastRet = -1;     
int expectedModCount = modCount;

cursor,lastRet定義からlastRetはcursorよりずっと少ないのでhasNext()の実現方法は異常に簡単であり,cursorとlastRetが等しいか否かを判断するだけでよい.
public boolean hasNext() {            
     return cursor != size;
        }

next()実装は、cursorインデックスの位置にある要素を返し、cursor、lastRetを変更すれば簡単です.
public E next() {
            checkForComodification();
            int i = cursor;    //      
            if (i >= size)    //              ,     
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;      //cursor + 1
            return (E) elementData[lastRet = i];  //lastRet + 1    cursor   
        }

checkForComodification()は,主に集合の修正回数が合法であるか,すなわち遍歴中に集合が修正されたか否かを判断するために用いられる.modCountは、ArrayListコレクションの修正回数を記録するために用いられ、0に初期化され、コレクションが修正されるたびに(構造上の修正、内部updateは算入されない)、add、removeなどの方法でmodCount+1となるので、modCountが変わらなければ、コレクション内容が修正されていないことを示す.このメカニズムは主にArrayList集合を実現するための高速失敗メカニズムであり,Javaの集合の多くは高速失敗メカニズムが存在するが,ここでは多くは言わないが,後述する.だから、遍歴中にエラーが発生しないことを保証するには、遍歴中に集合に対して構造的な修正が発生しないことを保証しなければならない(もちろんremove方法を除く)、異常なエラーが発生した場合、catch後に処理しないのではなく、プログラムがエラーしているかどうかを真剣にチェックしなければならない.
final void checkForComodification() {            
                 if (modCount != expectedModCount)                
                 throw new ConcurrentModificationException();
        }

remove()メソッドの実装では、ArrayList自体のremove()メソッドを呼び出してlastRet位置要素を削除し、modCountを変更すればよい.
public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

ここでArrayListのIterator実装について説明します.Hashset、TreeSetなどの集合のIterator実装について、興味があれば研究を続けてみてください.個人的には、これらの集合のソースコードを研究する前に、その集合のデータ構造を明確に認識する必要があると思います.これにより、仕事の半分以上の効果が得られます!!!