Heim >Java >javaLernprogramm >Detaillierte Erläuterung der for-each-Schleife und Iteration in JAVA

Detaillierte Erläuterung der for-each-Schleife und Iteration in JAVA

高洛峰
高洛峰Original
2017-01-21 16:43:061409Durchsuche

Beim Erlernen der Sammlung in Java ist mir aufgefallen, dass die Root-Schnittstelle Collection der Sammlungsebene die Iterable8742468051c85b06f0a0af9e3e506b5c-Schnittstelle implementiert (befindet sich im java.lang-Paket). Durch die Implementierung dieser Schnittstelle kann das Objekt zum Ziel von „ werden. foreach“-Anweisung und die einzige in der Schnittstelle implementierte Methode besteht darin, einen Iterator zurückzugeben, der über eine Menge von Elementen vom Typ T iteriert.

1. Iterator Iterator

Schnittstelle: Iterator8742468051c85b06f0a0af9e3e506b5c

public interface Iterator<E>{
  boolean hasNext();
 E next();
 void remove();
 }

Wenn Sie sich die Iterator-Schnittstellen-API ansehen, können Sie es wissen dass dies ein Iterator zum Durchlaufen der Sammlung ist. Iteratoren ermöglichen es dem Aufrufer, mithilfe einer genau definierten Semantik Elemente aus der Sammlung zu entfernen, auf die der Iterator während der Iteration zeigt.

Besonders hervorzuheben ist die Verwendung der Methode „remove()“ dieses Iterators: Entfernen des letzten vom Iterator zurückgegebenen Elements (optionale Operation) aus der Sammlung, auf die der Iterator zeigt. Diese Methode kann nur einmal pro Aufruf von next aufgerufen werden. Wenn die Sammlung, auf die der Iterator zeigt, während einer Iteration anders als durch den Aufruf dieser Methode (Remove-Methode) geändert wird, ist das Verhalten des Iterators undefiniert. Der Schnittstellendesigner hat beim Entwerfen der Iterator8742468051c85b06f0a0af9e3e506b5c-Schnittstelle darauf hingewiesen, dass der Aufruf einer anderen Methode „remove()“ als des Iterators zum Ändern der Sammlung, auf die der Iterator während der Iteration zeigt, ungewisse Konsequenzen hat. Die spezifischen Konsequenzen hängen von der spezifischen Implementierung des Iterators ab. Als Reaktion auf die möglichen Situationen, in denen solche ungewissen Konsequenzen auftreten können, bin ich beim Erlernen von ArrayList auf eine davon gestoßen: Der Iterator hat eine ConcurrentModificationException-Ausnahme ausgelöst. Die spezifische Ausnahmesituation wird im folgenden Code dargestellt:

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

Dieser Code löst beim Ausführen eine ConcurrentModificationException-Ausnahme aus, da wir während des Iterators keine Verwendung haben Führen Sie die Methode „remove()“ von Iterator aus, um Elemente zu löschen, verwenden Sie jedoch die Methode „remove()“ von ArrayList, um die Sammlung zu ändern, auf die der Iterator zeigt. Dies verstößt gegen die Designprinzipien von Iteratoren, sodass eine Ausnahme auftritt.

Die gemeldete Ausnahme lautet wie folgt:

Ausnahme im Thread „main“ java.util.ConcurrentModificationException
bei java.util.ArrayList$Itr.checkForComodification ( ArrayList.java:859)
bei java.util.ArrayList$Itr.next(ArrayList.java:831)
bei Text.ItaratorTest.main(ItaratorTest.java:17)

2. for-each-Schleife und Iterator Iterator8742468051c85b06f0a0af9e3e506b5c

Ab Java5 gibt es in Java eine for-each-Schleife, mit der Sammlungen und Arrays durchlaufen werden können. Mit der Foreach-Schleife können Sie die Sammlung durchlaufen, ohne den Index in einer herkömmlichen for-Schleife verwalten zu müssen oder ohne die hasNext()-Methode in der while-Schleife aufzurufen, wenn Sie iterator / ListIterator (eine Iteratorimplementierung in ArrayList) verwenden. Die for-each-Schleife vereinfacht das Durchlaufen einer Sammlung oder eines Arrays. Bei der Verwendung einer foreach-Schleife sind jedoch zwei Punkte zu beachten.

Objekte, die eine foreach-Schleife verwenden, müssen die Iterable8742468051c85b06f0a0af9e3e506b5c-Schnittstelle implementieren

Bitte sehen Sie sich das folgende Beispiel an:

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

Der obige Code wird nicht kompiliert, da die CustomCollection-Klasse im Code die Iterable8742468051c85b06f0a0af9e3e506b5c-Schnittstelle nicht implementiert. Der beim Kompilieren gemeldete Fehler ist wie folgt:

Ausnahme im Thread „. main" java .lang.Error: Ungelöstes Kompilierungsproblem:
Kann nur über ein Array oder eine Instanz von java.lang.Iterable

bei Text.ForeachTest1.main(ForeachTest1.java:15)

Tatsächlich muss man nicht bis zur Kompilierung warten, um den Fehler zu finden. Nach dem Schreiben dieses Codes zeigt Eclipse den Fehler in der foreach-Schleife an: Kann nur über ein Array oder eine Java-Instanz iterieren. lang.Iterable

Was aus dem obigen Beispiel noch einmal bestätigt werden kann, ist, dass die foreach-Schleife nur für Objekte gilt, die die Iterable8742468051c85b06f0a0af9e3e506b5c-Schnittstelle implementieren. Da alle integrierten Collection-Klassen die java.util.Collection-Schnittstelle implementieren und Iterable geerbt haben, können Sie zur Lösung der oben genannten Probleme wählen, ob CustomCollection einfach die Collection-Schnittstelle implementieren oder AbstractCollection erben soll. Die Lösung lautet wie folgt:

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. Die interne Implementierung der foreach-Schleife basiert ebenfalls auf Iterator

Um zu überprüfen, ob die foreach-Schleife Iterator verwendet Als interne Implementierung verwenden wir tatsächlich immer noch das Beispiel am Anfang dieses Artikels zur Überprüfung:

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

Ausnahme gemeldet, wenn das Programm ausgeführt wird:


Ausnahme im Thread „main“ java.util.ConcurrentModificationException

bei java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
bei java.util.ArrayList$Itr.next( ArrayList.java:831)
bei Text .ItaratorTest.main(ItaratorTest.java:22)

Diese Ausnahme zeigt, dass der Iterator innerhalb der for-each-Schleife verwendet wird, um die Sammlung zu durchlaufen Ruft außerdem Iterator.next() auf, das Änderungen (des Elements) überprüft und eine ConcurrentModificationException auslöst.

Zusammenfassung:

Wenn Sie beim Durchlaufen einer Sammlung die Sammlung während des Durchlaufens ändern möchten, müssen Sie dies über Iterator/listIterator tun, da sonst „unbestimmte Konsequenzen“ auftreten können.


Die foreach-Schleife wird durch einen Iterator implementiert. Das Objekt, das die foreach-Schleife verwendet, muss die Iterable-Schnittstelle implementieren


Das Obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, dass er zum Lernen aller beiträgt. Ich hoffe auch, dass jeder die PHP-Chinesisch-Website unterstützt.

Ausführlichere Artikel zu for-each-Schleifen und Iterationen in JAVA finden Sie auf der chinesischen PHP-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
Vorheriger Artikel:Regeln für Java-DatentypenNächster Artikel:Regeln für Java-Datentypen