Heim  >  Artikel  >  Java  >  Detaillierte Erläuterung des Beispielcodes des Iterators im Java Collection Framework

Detaillierte Erläuterung des Beispielcodes des Iterators im Java Collection Framework

黄舟
黄舟Original
2017-03-21 10:30:131269Durchsuche

Dieser Artikel bietet Ihnen hauptsächlich eine kurze Einführung in die relevanten Informationen zum Iterator im Java-Sammlungsframework. Er hat einen bestimmten Referenzwert.

Array-Daten in Java können abgerufen werden Index, was ist mit Objekten? Auch über den Index? Heute analysieren wir die Methode iteration-Iterator zum Abrufen von Sammlungsobjekten in Java-Sammlungen.

Dieser Artikel analysiert hauptsächlich den Iterator-Teil im Java-Sammlungs-Framework, Iterator. Die Quellcode-Analyse basiert auf JDK1.8, Analysetool, AndroidStudio. Bitte korrigieren Sie mich, wenn die Artikelanalyse Mängel aufweist.

1. Einführung

Wir verwenden häufig die von JDK bereitgestellte Iterationsschnittstelle, um Java-Sammlungen zu iterieren.

 Iterator iterator = list.iterator();
      while(iterator.hasNext()){
        String string = iterator.next();
        //do something
      }

Das Obige ist die grundlegende Vorlage, die von Iteratoren verwendet wird. Tatsächlich können wir Iteration einfach als Durchquerung verstehen, eine standardisierte Methodenklasse zum Durchlaufen aller Objekte in verschiedenen Containern. Es steuert immer den Iterator und sendet ihm die Befehle „vorwärts“, „rückwärts“ und „aktuelles Element abrufen“, um indirekt die gesamte Sammlung zu durchlaufen. In Java ist Iterator eine Schnittstelle, die nur Grundregeln für die Iteration bereitstellt:

  public interface Iterator<E> {
  //判断容器内是否还有可供访问的元素
  boolean hasNext();
  //返回迭代器刚越过的元素的引用,返回值是 E
  E next();
  //删除迭代器刚越过的元素
  default void remove() {
    throw new UnsupportedOperationException("remove");
  }
}

Das Obige ist die Grunddeklaration eines Iterators. Wir analysieren ihn anhand spezifischer Sammlungen.

2. Klassifizierung der Sammlung

2.1 Iterator von ArrayList

Durch die Analyse des Quellcodes von ArrayList können wir erkennen, dass darin zuerst eine interne Klasse definiert ist ArrayList Itr, diese innere Klasse implementiert die Iterator-Schnittstelle wie folgt:

private class Itr implements Iterator<E> {
  //....
}

Die innere Klasse implementiert die Iterator-Schnittstelle, und der Iterator von ArrayList gibt seine innere Klasse Itr zurück, also wir Sehen Sie sich hauptsächlich an, wie Itr implementiert wird.

  public Iterator<E> iterator() {
    return new Itr();
  }

Als nächstes analysieren wir die Implementierung seiner internen Klasse 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();
    }
  }

Als Erstes analysieren wir die definierte -Variable:

    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;

Unter diesen ist „Limit“ die Größe der aktuellen ArrayList und der Cursor stellt das nächste Element dar . Index und lastRet ist der Index des vorherigen Elements. Wenn nicht, ist die Rückgabe von -1 von geringem Nutzen. Wir werden dann analysieren und sehen, wie wir feststellen können, ob es während der Iteration nachfolgende Elemente gibt.

  public boolean hasNext() {
      return cursor < limit;
  }

Es ist ganz einfach, festzustellen, ob der Index des nächsten Elements die Kapazität des Arrays erreicht hat. Wenn dies der Fall ist, ist es vorbei.

Als nächstes analysieren wir die Methode zum Erhalten des Elements des aktuellen Index

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

Warum müssen wir modCount in der nächsten Methode bestimmen? Das heißt, es wird verwendet, um zu bestimmen, ob die Sammlung während des Durchlaufprozesses geändert wurde. modCount wird verwendet, um die Anzahl der Änderungen der ArrayList-Sammlung aufzuzeichnen. Es wird auf 0 initialisiert. Immer wenn die Sammlung geändert wird (Änderungen an der Struktur, interne Aktualisierungen werden nicht gezählt), wie z. B. Hinzufügen, Entfernen und andere Methoden, gilt modCount + 1 Wenn also modCount unverändert bleibt, bedeutet dies, dass der Sammlungsinhalt nicht geändert wurde. Dieser Mechanismus wird hauptsächlich zur Implementierung des Schnellfehlermechanismus der ArrayList-Sammlung verwendet. Unter den Java-Sammlungen verfügt ein großer Teil der Sammlungen über Schnellfehlermechanismen. Um sicherzustellen, dass während des Durchlaufvorgangs keine Fehler auftreten, sollten wir daher sicherstellen, dass während des Durchlaufvorgangs keine strukturellen Änderungen an der Sammlung vorgenommen werden (außer natürlich bei der Entfernungsmethode). Wenn ein ungewöhnlicher Fehler auftritt, sollten wir dies sorgfältig prüfen ob das Programm Fehler hat statt Keine Verarbeitung erfolgt nach Catch. Der obige Code ist relativ einfach, er gibt lediglich den Array-Wert am Index zurück.

Bei der Iterationsmethode von ArrayList wird hauptsächlich der Wert des Index beurteilt und mit der Größe des Arrays verglichen, um festzustellen, ob keine Daten zum Durchlaufen vorhanden sind, und dann werden die Werte in abgerufen Das Array wiederum erfasst hauptsächlich die zugrunde liegende Implementierung.

Als nächstes analysieren wir die Iterator-Methode von HashMap. Andere Methoden sind ähnlich, solange Sie die zugrunde liegende Implementierung verstehen.

2.2 HashMap's Iterator

In HashMap gibt es auch eine Klasse, die die Iterator-Schnittstelle implementiert, aber es ist nur eine abstrakte Klasse, Werfen wir einen Blick darauf wie es umgesetzt wird.

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

In ähnlicher Weise definiert es auch eine Variable

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

next, die den nächsten Eintragsknoten darstellt. ExpectedModCount wird auch verwendet, um den geänderten Status zu bestimmen Schneller Fail-Mechanismus für Sammlungen. Der Index stellt den aktuellen Index dar, und der Knoteneintrag wird durch den aktuellen Index dargestellt. Schauen wir uns an, wie ermittelt wird, ob für das nächste Element ein Wert vorhanden ist.

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

Es ist sehr einfach festzustellen, ob next null ist. Wenn es null ist, bedeutet dies, dass keine Daten vorhanden sind.

Analysieren Sie dann die Methode zur Gewinnung von Elementen

    final Entry<K,V> nextEntry() {
      if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
      HashMapEntry<K,V> e = next;
      if (e == null)
        throw new NoSuchElementException();
      // 一个Entry就是一个单向链表
      // 若该Entry的下一个节点不为空,就将next指向下一个节点;
      // 否则,将next指向下一个链表(也是下一个Entry)的不为null的节点。
      if ((next = e.next) == null) {
        HashMapEntry[] t = table;
        while (index < t.length && (next = t[index++]) == null)
          ;
      }
      current = e;
      return e;
    }

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung des Beispielcodes des Iterators im Java Collection Framework. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn