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?
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.
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();
}
};
}
}
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.
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 .