web-dev-qa-db-fra.com

Implémenter Java Iterator et Iterable dans la même classe?

J'essaie de comprendre les interfaces Iterator et Iterable en Java

J'écris ce cours

class MyClass implements Iterable<String> {
    public String[] a = null;
    public MyClass(String[] arr) {
        a = arr;    
    }

    public MyClassIterator iterator() {
        return new MyClassIterator(this);
    }

    public class MyClassIterator implements Iterator<String> {
        private MyClass myclass = null;
        private int count = 0;
        public MyClassIterator(MyClass m) {
            myclass = m;    
        }

        public boolean hasNext() {
            return count < myclass.a.length;
        }
        public String next() {
            int t = count;
            count++;
            return myclass.a[t];
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }
    }   
}

Cela semble fonctionner.

Devrais-je avoir:

Myclass implements Iterable<Stirng>, Iterator<String> {

}

Ou je devrais mettre MyClassIterator en dehors de MyClass comme

class MyClass implements Iterable<String> {
    public String[] a = null;
    public MyClass(String[] arr) {
        a = arr;    
    }
    public MyClassIterator iterator() {
        return new MyClassIterator(this);
    }
}


    public class MyClassIterator implements Iterator<String> {
        private MyClass myclass = null;
        private int count = 0;
        public MyClassIterator(MyClass m) {
            myclass = m;    
        }

        public boolean hasNext() {
            return count < myclass.a.length;
        }
        public String next() {
            int t = count;
            count++;
            return myclass.a[t];
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }
    }   

Quel est le meilleur?

15
icn

Vous devriez presque jamais implémenter Iterable et Iterator dans la même classe. Ils font des choses différentes. Un itérateur est naturellement stateful - au fur et à mesure que vous l'utilisez, il doit mettre à jour sa vision du monde. Un itérable, cependant, n'a besoin que de pouvoir créer de nouveaux itérateurs. En particulier, vous pouvez avoir plusieurs itérateurs travaillant sur le même original, itérable en même temps.

Votre approche actuelle est plutôt satisfaisante - il y a des aspects de la mise en œuvre que je changerais, mais ça va pour la séparation des responsabilités.

33
Jon Skeet

Vous étiez sur la bonne voie avec votre premier essai. MyClass n'a besoin que d'implémenter Iterable<String>, ce qui vous oblige à fournir une implémentation Iterator<String> à renvoyer à partir de Iterable<String>.iterator()

Il n'est pas nécessaire de placer MyClassIterator en dehors de MyClass car dans la plupart des cas, vous n'aurez même jamais besoin d'utiliser directement le Iterator<String> (il est utilisé implicitement par la syntaxe for .. in .. sur Iterable<String>s), et dans tous les autres cas, l'interface est suffisante, sauf si vous ajoutez réellement comportement à la mise en œuvre (ce que vous n’aurez probablement jamais besoin de faire).

Voici comment je le ferais, voir les commentaires en ligne:

import Java.util.Iterator;

class MyClass implements Iterable<String>{
    public String[] a=null; //make this final if you can
    public MyClass(String[] arr){
        a=arr; //maybe you should copy this array, for fear of external modification
    }

    //the interface is sufficient here, the outside world doesn't need to know
    //about your concrete implementation.
    public Iterator<String> iterator(){
        //no point implementing a whole class for something only used once
        return new Iterator<String>() {
            private int count=0;
            //no need to have constructor which takes MyClass, (non-static) inner class has access to instance members
            public boolean hasNext(){
                //simplify
                return count < a.length;
            }
            public String next(){
                return a[count++]; //getting clever
            }

            public void remove(){
                throw new UnsupportedOperationException();
            }
        };
    }
}
7
Stephen Swensen

Vous ne devriez pas faire Myclass implements Iterable<String>,Iterator<String>{ car les itérateurs sont à usage unique. À l'exception des itérateurs de liste, il n'y a aucun moyen de les renvoyer au début.

Par ailleurs, vous pouvez ignorer le

MyClass myClass;
public MyClassInterator(MyClass m){
  myclass=m;  
}

et au lieu de référencer

myClass

référence

MyClass.this

Votre classe interne n'est pas statique, donc MyClass.this fera référence à l'instance de la classe englobante qui l'a créée.

2
Mike Samuel

Je suppose que c’est un moyen standard d’implémenter à la fois Iterable et Iterator.

// return list of neighbors of v
public Iterable<Integer> adj(int v) {
    return new AdjIterator(v);
}

// support iteration over graph vertices
private class AdjIterator implements Iterator<Integer>, Iterable<Integer> {
    private int v;
    private int w = 0;

    AdjIterator(int v) {
        this.v = v;
    }

    public Iterator<Integer> iterator() {
        return this;
    }

    public boolean hasNext() {
        while (w < V) {
            if (adj[v][w]) return true;
            w++;
        }
        return false;
    }

    public Integer next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }
        return w++;
    }

Référence https://algs4.cs.princeton.edu/41graph/AdjMatrixGraph.Java.html .

0
8cold8hot