ホームページ  >  記事  >  Java  >  Javaコレクションフレームワークのイテレータのサンプルコードの詳細説明

Javaコレクションフレームワークのイテレータのサンプルコードの詳細説明

黄舟
黄舟オリジナル
2017-03-21 10:30:131261ブラウズ

この記事では、主に 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 は、変更された

status を決定するためにも使用され、コレクションの高速失敗メカニズムとして使用されます。 Index は現在のインデックスを表し、現在のインデックスによってノード エントリが表されます。次の要素の値があるかどうかを確認する方法を見てみましょう。

    HashMapEntry<K,V> next;    // next entry to return
    int expectedModCount;  // For fast-fail
    int index;       // current slot
    HashMapEntry<K,V> current;   // current entry

next が null かどうかを判断するのは非常に簡単です。null の場合は、データが存在しないことを意味します。

次に要素の取得方法を分析します

    public final boolean hasNext() {
      return next != null;
    }

以上がJavaコレクションフレームワークのイテレータのサンプルコードの詳細説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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