Des itérators et des boucles

Nos open-spaces sont souvent lieux de discussion à propos des parcours de liste, notamment lorsqu’on ne dispose que de l’itérator sur la liste comme c’est parfois le cas dans certaines lib.

Pour illustrer tout ça, prenons en compte la liste d’amis, définie de la manière suivante dans un test (version simplifiée).

1
2
3
4
5
6
7
8
9
10
11
private List<String> amis;
 
@Before
public void doBefore() {
    amis = newArrayList(); // cf. tuto Google-Collections
    amis.add("Lucie");
    amis.add("Jean");
    amis.add("Paul");
    amis.add("Marion");
    amis.add("Thierry");
}

Vous avez, depuis Java 5, l’habitude de parcourir la liste à l’aide d’un « foreach » :

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void testAmis() {
    System.out.println(">>> testAmis");
 
    System.out.println(amis);
    // --> [Lucie, Jean, Paul, Marion, Thierry]
 
    for (String ami : amis) {
         System.out.println(ami);
    }
 
    Assert.assertTrue("La liste d'amis n'a pas la bonne taille", amis.size() == 5);
}

Et pour coller un peu plus au cas décrit, on se force à utiliser une méthode qui renvoie directement un itérator, comme suit :

1
2
3
private Iterator<String> amisIterator() {
    return amis.iterator();
}

Or sur un itérator, on ne peut pas utiliser de « foreach », puisqu’il n’est pas « itérable » ! Du coup on en revient aux « bases » d’avant Java 5 et on peut parcourir la liste (ie. l’itérator) de « 2 » manières classiques.

Soit à l’aide d’un while :

1
2
3
4
5
6
7
8
9
10
11
@Test
public void testWhile() {
 
    System.out.println(">>> testWhile");
 
    Iterator<String> it = amisIterator();
    while (it.hasNext()) {
         String ami = it.next();
         System.out.println(ami);
    }
}

Soit à l’aide d’une boucle for, qui ressemble presque au while :

1
2
3
4
5
6
7
8
9
10
@Test
public void testFor() {
 
    System.out.println(">>> testFor");
 
    for (Iterator<String> it = amisIterator(); it.hasNext();) {
         String ami = it.next();
         System.out.println(ami);
    }
}

Certains diront que le while est « syntaxiquement plus logique » tandis que d’autres s’intéresseront à la portée limitée de l’itérator dans la boucle for. Et ci et ça. [..] Mais n’allons pas plus loin car ça peut durer longtemps et ça divise les équipes. Le plus simple n’est-il pas simplement de rendre l’itérator [..] itérable ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static <E> Iterable<E> iterable(final Iterator<E> iterator) {
    return new Iterable<E>() {
        public Iterator<E> iterator() {
             return iterator;
        }
    };
}
 
@Test
public void testForEach() {
    System.out.println(">>> testForEach");
 
    for (String ami : iterable(amisIterator())) {
         System.out.println(ami);
    }
}

C’est magique ? A vous de me dire.

3 réflexions au sujet de « Des itérators et des boucles »

  1. Avatar de thierrylerthierryler Auteur de l’article

    Bonne question. Il se trouve qu’on travaille (là où je suis) avec des lib qui nous donnent des itérators directement. Je ne sais pas pourquoi. Toutes les raisons que je peux imaginer ne s’applique pas à ce cas donc… On fait avec. Du coup soit on utilise la méthode iterate(), qui peut être appliquée à d’autres types comme tu le signales, soit on l’englobe directement… C’est un peu au choix…

  2. Avatar de adigubaadiguba

    Salut,

    Pourquoi ne pas simplement retourner la liste déclarée en Iterable :

    1
    2
    3
    private Iterable amisIterable() { &nbsp;<br />
    &nbsp; return amis; &nbsp;<br />
    }

    Si c’est pour une question de sécurité (afin d’éviter les casts méchant), il suffit d’englober cela dans une nouvelle instance d’Iterable :

    1
    2
    3
    4
    5
    6
    7
    private Iterable amisIterable() { &nbsp;<br />
    &nbsp; return new Iterable() {&nbsp;<br />
    &nbsp;   @Overrides public Iterator iterator() {&nbsp;<br />
    &nbsp;     return amis.iterator();&nbsp;<br />
    &nbsp;   }&nbsp;<br />
    &nbsp;  };&nbsp;<br />
    }

    Bien sûr le code de la méthode iterable() reste utile avec les APIs existantes retournant déjà un Iterator. Et la même astuce s’applique également à d’autres types d’objets (Enumeration par exemple)

    a++

Laisser un commentaire