Maison  >  Article  >  Java  >  Introduction détaillée à Iterable et Iterator en Java

Introduction détaillée à Iterable et Iterator en Java

不言
不言avant
2018-10-08 15:37:533000parcourir

Cet article vous apporte une introduction détaillée à Iterable et Iterator en Java. Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer.

En Java, nous pouvons parcourir la collection List des manières suivantes :

List<Integer> list = new ArrayList<>();
list.add(5);
list.add(23);
list.add(42);
for (int i = 0; i < list.size(); i++) {
    System.out.print(list.get(i) + ",");
}

Iterator it = list.iterator();
while (it.hasNext()) {
    System.out.print(it.next() + ",");
}

for (Integer i : list) {
    System.out.print(i + ",");
}

La première est une boucle for ordinaire, la seconde est une traversée d'itérateur et les Trois sont les pour chaque boucle. Les deux dernières méthodes impliquent des objets itérateurs et itérables en Java. Examinons ensuite les différences entre ces deux objets et comment implémenter une boucle for each dans une classe personnalisée.

Iterator et Iterable

iterator est un objet itérateur en Java, qui est la dépendance sous-jacente qui peut parcourir une collection telle que List. L'interface itérable définit une méthode qui renvoie l'itérateur, ce qui équivaut à encapsuler l'itérateur. En même temps, les classes qui implémentent l'interface itérable peuvent prendre en charge chaque boucle.

Détails internes de l'Iterator

Les principales méthodes de l'interface Iterator dans jdk sont les suivantes :

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

Iterator définit la méthode d'accès itératif à la collection via ce qui précède deux méthodes, et l'implémentation spécifique dépend de différentes classes d'implémentation. La classe de collection spécifique implémente les méthodes dans l'interface Iterator pour implémenter l'itération.

On peut constater que l'interface Iterator n'est pas implémentée dans List, mais que l'interface Iterable est implémentée. Une observation plus approfondie du code source de l'interface Iterable montre qu'elle renvoie simplement un objet Iterator.

public interface Iterable<T> {
  Iterator<T> iterator();
}

On peut donc utiliser la méthode suivante pour itérer la List (en appelant la méthode iterator())

Iterator it = list.iterator();
while (it.hasNext()) {
    System.out.print(it.next() + ",");
}

En même temps, si vous implémentez l'interface Iterable, vous pouvez également utiliser la boucle for each.

Principe de for each

En fait, la boucle for each s'appuie également sur l'itérateur Iterator, mais le sucre syntaxique fourni par Java, le compilateur Java le convertira en itérateur Iterator pour le parcours . Nous décompilons ce qui suit pour chaque boucle :

 for (Integer i : list) {
       System.out.println(i);
   }

Après la décompilation :

Integer i;
for(Iterator iterator = list.iterator(); iterator.hasNext(); System.out.println(i)){
        i = (Integer)iterator.next();        
    }

Vous pouvez voir que Java pour chaque boucle améliorée est implémenté via l'itérateur.

Discussion approfondie de la relation entre Iterable et Iterator

J'ai une question, pourquoi ne pas simplement mettre les méthodes hasNext() et next() directement dans l'interface Iterable et d'autres classes peut-on les mettre en œuvre directement ?

La raison est que certaines classes de collection peuvent avoir plus d'une méthode de parcours. Une classe qui implémente Iterable peut implémenter plusieurs classes internes Iterator, telles que les deux classes internes LinkedList et ListItr dans DescendingIterator. Le parcours bidirectionnel et le parcours inverse sont implémentés respectivement. Implémentez différentes méthodes de parcours en renvoyant différents Iterator, ce qui est plus flexible. Si vous fusionnez les deux interfaces, vous ne pouvez pas renvoyer des classes d'implémentation Iterator différentes. Le code source pertinent de ListItr est le suivant :

    public ListIterator<E> listIterator(int index) {
        checkPositionIndex(index);
        return new ListItr(index);
    }

    private class ListItr implements ListIterator<E> {
        ...
        ListItr(int index) {
            // assert isPositionIndex(index);
            next = (index == size) ? null : node(index);
            nextIndex = index;
        }

        public boolean hasNext() {
            return nextIndex < size;
        }
        ...

Comme indiqué ci-dessus, l'itérateur peut être renvoyé en appelant la méthode list.listIterator() (list.iterator() n'est que son implémentation par défaut)

DescendingIteratorLe code source est le suivant :

    public Iterator<E> descendingIterator() {
        return new DescendingIterator();
    }
    private class DescendingIterator implements Iterator<E>     {
        private final ListItr itr = new ListItr(size());
        public boolean hasNext() {
            return itr.hasPrevious();
        }
        public E next() {
            return itr.previous();
        }
        public void remove() {
            itr.remove();
        }
    }

Cet itérateur peut également être utilisé via list.descendingIterator().

Implémentez votre propre itérateur

Nous avons maintenant une classe personnalisée ArrayMap, maintenant si nous la parcourons pour chacun comme suit :

ArrayMap<String, Integer> am = new ArrayMap<>();
am.put("hello", 5);
am.put("syrups", 10);

for (String s: am) {
   System.out.println(s);
}

Puisque nous ne l'avons pas implémenté hashNext et ensuite les méthodes abstraites, elles ne peuvent donc pas être parcourues.

Classe d'itérateur personnalisée

Nous personnalisons d'abord une classe d'itérateur pour implémenter les méthodes hashNext et next, et l'utilisons comme classe interne d'ArrayMap. Le code pertinent est le suivant :

   public class KeyIterator implements Iterator<K> {
        private int ptr;

        public KeyIterator() {
            ptr = 0;
        }

        @Override
        public boolean hasNext() {
            return (ptr != size);
        }

        @Override
        public K next() {
            K returnItem = keys[ptr];
            ptr += 1;
            return returnItem;
        }
    }
Vous pouvez voir que la règle de traversée que nous avons spécifiée dans la suite consiste à parcourir en fonction de la valeur clé d'ArrayMap. Avec la classe itérateur ci-dessus, nous pouvons utiliser la méthode itérateur pour le parcourir en externe. Le code de traversée est le suivant :

ArrayMap<String, Integer> am = new ArrayMap<>();
am.put("hello", 5);
am.put("syrups", 10);
ArrayMap.KeyIterator ami = am.new KeyIterator();
while (ami.hasNext()) {
    System.out.println(ami.next());
}
Comme indiqué ci-dessus, l'accès itératif est effectué en créant un objet KeyIterator (notez l'objet externe). objets de classe internes de création de classe).

Prise en charge de chaque boucle

Nous ne pouvons pas prendre en charge l'accès à chaque boucle pour le moment car nous n'avons pas implémenté l'interface itérable. Implémentez d'abord l'interface Iterable dans ArrayMap :

public class ArrayMap<K, V> implements Iterable<K> {

    private K[] keys;
    private V[] values;
    int size;

    public ArrayMap() {
        keys = (K[]) new Object[100];
        values = (V[]) new Object[100];
        size = 0;
    }
  ....
}
Remplacez ensuite la méthode iterator() et renvoyez notre propre objet itérateur (iterator)

    @Override
    public Iterator<K> iterator() {
        return new KeyIterator();
    }
Notez que notre classe KeyIterator personnalisée doit implémenter l'interface Iterator, sinon dans la méthode iterator() les types renvoyés ne correspondent pas .

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer