この記事では、主に Java コレクション フレームワークのイテレーターに関する関連情報を簡単に紹介します。興味のある方は参照してください。
Java の配列データはインデックスを通じて取得できます。物体?インデックス経由でも?今日は、Java コレクション内のコレクション オブジェクトを取得するメソッド iteration-Iterator を分析します。
この記事は主にJavaコレクションフレームワークIteratorのイテレータ部分を分析しています。ソースコード分析はJDK1.8、分析ツールAndroidStudioに基づいています。記事の分析に不備があれば修正してください。
1. はじめに
Java コレクションを反復するために、JDK が提供する反復インターフェースをよく使用します。
Iterator iterator = list.iterator(); while(iterator.hasNext()){ String string = iterator.next(); //do something }
上記は、イテレーターによって使用される基本的なテンプレートです。実際、反復は、さまざまなコンテナー内のすべてのオブジェクトを走査するための標準化されたメソッド クラスであるトラバーサルとして単純に理解できます。これは常に Iterator を制御し、「forward」、「backward」、および「get current element」コマンドを送信して、コレクション全体を間接的に走査します。 Java では、イテレーターは反復のための基本的なルールのみを提供するインターフェースです:
public interface Iterator<E> { //判断容器内是否还有可供访问的元素 boolean hasNext(); //返回迭代器刚越过的元素的引用,返回值是 E E next(); //删除迭代器刚越过的元素 default void remove() { throw new UnsupportedOperationException("remove"); } }
上記はイテレーターの基本的な宣言です。特定のコレクションを通じてそれを分析します。
2. コレクションの分類
2.1 ArrayList のイテレータ
ArrayList のソース コードを分析すると、ArrayList の内部で、次のように、Iterator インターフェイスを実装する内部クラス Itr を最初に定義していることがわかります。
内部的に クラスはIterator インターフェース を実装しており、ArrayList の Iterator は内部クラス Itr を返すため、主に Itr がどのように実装されているかを見ていきます。
private class Itr implements Iterator<E> { //.... }次に、その内部クラス Itr の実装を分析しましょう。
public Iterator<E> iterator() { return new Itr(); }まず、定義された
private class Itr implements Iterator<E> { protected int limit = ArrayList.this.size; int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; public boolean hasNext() { return cursor < limit; } @SuppressWarnings("unchecked") public E next() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); int i = cursor; if (i >= limit) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } public void remove() { if (lastRet < 0) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; limit--; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } @Override @SuppressWarnings("unchecked") public void forEachRemaining(Consumer<? super E> consumer) { Objects.requireNonNull(consumer); final int size = ArrayList.this.size; int i = cursor; if (i >= size) { return; } final Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) { throw new ConcurrentModificationException(); } while (i != size && modCount == expectedModCount) { consumer.accept((E) elementData[i++]); } // update once at end of iteration to reduce heap write traffic cursor = i; lastRet = i - 1; if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }そのうち、limitは現在のArrayListのサイズ、cursorは次の要素のインデックスを表し、そうでない場合は、lastRetは前の要素のインデックスを表します。 return - 1. ExpectModCount はほとんど役に立ちません。次に、反復中に後続の要素があるかどうかを判断する方法を分析して確認します。
protected int limit = ArrayList.this.size; int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount;とても簡単で、次の要素のインデックスが配列の容量に達したかどうかを判断するだけです。 次に、現在のインデックスの要素を取得するためにnextメソッドを解析してみましょう
public boolean hasNext() { return cursor < limit; }なぜnextメソッドでmodCountを判定する必要があるのでしょうか?つまり、トラバーサル プロセス中にコレクションが変更されたかどうかを判断するために使用されます。 modCount は、ArrayList コレクションの変更数を記録するために使用されます。これは 0 に初期化されます。追加、削除、その他のメソッドなど、コレクションが変更されるたびに (構造の変更、内部更新はカウントされません)、modCount + 1 になります。したがって、modCount が変化しない場合は、コレクションのコンテンツが変更されていないことを意味します。このメカニズムは主に、ArrayList コレクションの高速失敗メカニズムを実装するために使用されます。Java コレクションのうち、大部分のコレクションには高速失敗メカニズムがあります。したがって、走査プロセス中にエラーが発生しないようにするには、走査プロセス中にコレクションに構造的な変更が加えられていないことを確認する必要があります (もちろん、remove メソッドは除きます)。異常なエラーが発生した場合は、注意深く確認する必要があります。 catch の後に処理は行われません。プログラムにエラーがあるかどうかを示します。上記のコードは比較的単純で、インデックスの配列値を返すだけです。 ArrayListの反復メソッドは主にインデックスの値を判断し、配列のサイズと比較して走査するデータがないかを確認し、配列内の値を順番に取得します。 . 主に各コレクションの基礎となる実装を把握します。 次に、
HashMapのIteratorメソッドを分析します。他のメソッドも同様で、基礎となる実装を把握するだけです。
2.2 HashMapのイテレータ HashMapには、単なる抽象クラスであるHashIteratorを実装するクラスもあります。どのように実装されるかを見てみましょう。
public E next() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); int i = cursor; if (i >= limit) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; }同様に、次のエントリのノードを表す変数
private abstract class HashIterator<E> implements Iterator<E> { HashMapEntry<K,V> next; // next entry to return int expectedModCount; // For fast-fail int index; // current slot HashMapEntry<K,V> current; // current entry HashIterator() { expectedModCount = modCount; if (size > 0) { // advance to first entry HashMapEntry[] t = table; while (index < t.length && (next = t[index++]) == null) ; } } public final boolean hasNext() { return next != null; } final Entry<K,V> nextEntry() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); HashMapEntry<K,V> e = next; if (e == null) throw new NoSuchElementException(); if ((next = e.next) == null) { HashMapEntry[] t = table; while (index < t.length && (next = t[index++]) == null) ; } current = e; return e; } public void remove() { if (current == null) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); Object k = current.key; current = null; HashMap.this.removeEntryForKey(k); expectedModCount = modCount; } }next も定義されています。expectedModCount は、変更された
HashMapEntry<K,V> next; // next entry to return int expectedModCount; // For fast-fail int index; // current slot HashMapEntry<K,V> current; // current entrynext が null かどうかを判断するのは非常に簡単です。null の場合は、データが存在しないことを意味します。 次に要素の取得方法を分析します
public final boolean hasNext() { return next != null; }
以上がJavaコレクションフレームワークのイテレータのサンプルコードの詳細説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。