Java foreach efficacité

J'ai quelque chose comme ceci:

Map<String, String> myMap = ...;

for(String key : myMap.keySet()) {
   System.out.println(key);
   System.out.println(myMap.get(key)); 
}

Donc myMap.keySet() est-il appelé une fois dans la boucle foreach ? Je pense que c'est, mais que vous voulez de votre avis.

Je voudrais savoir si l'utilisation de foreach de cette manière (myMap.keySet()) a un impact sur les performances ou est équivalente à ceci:

Set<String> keySet = myMap.keySet();
for (String key : keySet) {
   ...
}
22
demandé sur Carl Manaster 2009-05-25 00:30:56

6 réponses

Si vous voulez être absolument certain, compilez-le dans les deux sens et décompilez-le et comparez-le. Je l'ai fait avec la source suivante:

public void test() {
  Map<String, String> myMap = new HashMap<String, String>();

  for (String key : myMap.keySet()) {
    System.out.println(key);
    System.out.println(myMap.get(key));
  }

  Set<String> keySet = myMap.keySet();
  for (String key : keySet) {
    System.out.println(key);
    System.out.println(myMap.get(key));
  }
}

Et quand j'ai décompilé le fichier de classe avec Jad, j'obtient:

public void test()
{
    Map myMap = new HashMap();
    String key;
    for(Iterator iterator = myMap.keySet().iterator(); iterator.hasNext(); System.out.println((String)myMap.get(key)))
    {
        key = (String)iterator.next();
        System.out.println(key);
    }

    Set keySet = myMap.keySet();
    String key;
    for(Iterator iterator1 = keySet.iterator(); iterator1.hasNext(); System.out.println((String)myMap.get(key)))
    {
        key = (String)iterator1.next();
        System.out.println(key);
    }
}

Voilà ta réponse. Il est appelé une fois avec la forme for-loop.

65
répondu Eddie 2009-07-30 20:24:52

Il n'est appelé qu'une seule fois. En fait, il utilise un itérateur pour faire l'affaire.

De plus, dans votre cas, je pense que vous devriez utiliser

for (Map.Entry<String, String> entry : myMap.entrySet())
{
    System.out.println(entry.getKey());
    System.out.println(entry.getValue());
}

Pour éviter de chercher dans la carte à chaque fois.

35
répondu Valentin Rocher 2009-05-24 20:57:19

keySet() est appelé une seule fois. La "boucle for améliorée" est basée sur l'interface Iterable, qu'elle utilise pour obtenir un Iterator, qui est ensuite utilisé pour la boucle. Il n'est même pas possible d'itérer sur un Set d'une autre manière, car il n'y a pas d'index ou quoi que ce soit par lequel vous pourriez obtenir des éléments individuels.

Cependant, ce que vous devriez vraiment faire est d'abandonner complètement ce genre de soucis de micro-optimisation-si jamais vous avez de vrais problèmes de performance, la chance est d'environ 99% que c'est une chose à laquelle tu n'avais jamais pensé toute seule.

9
répondu Michael Borgwardt 2009-05-24 20:38:25

La réponse est dans la spécification du langage Java, pas besoin de décompiler:) C'est ce que nous pouvons lire à propos de l'instruction enhanced for :

L'renforcée pour l'instruction a forme:

EnhancedForStatement:
        for ( VariableModifiersopt Type Identifier: Expression) Statement

L'Expression doit soit avoir le type Iterable ou bien il doit être d'un type de tableau (§10.1), ou une compilation erreur se produit.

La portée d'une variable locale déclarée dans la partie FormalParameter d'un déclaration for améliorée (§14.14) être la déclaration contenue

La signification de l'amélioration for déclaration est donnée par traduction en une instruction de base for.

Si le type de Expression est un sous-type de Iterable, puis laissez I être le type de l'expression Expression.iterator(). L'instruction for améliorée est équivalente à une instruction de base for du forme:

for (I #i = Expression.iterator(); #i.hasNext(); ) {

        VariableModifiersopt Type Identifier = #i.next();
   Statement
}

#i est généré par le compilateur identificateur distinct de tout autres identifiants (générés par le compilateur ou autrement) qui sont dans le champ d'application (§6.3) au point où le amélioré pour déclaration se produit.

Sinon, l'Expression nécessairement a un type de tableau, T[]. Laissez L1 ... Lm être (éventuellement vide) de la séquence de étiquettes précédant immédiatement le instruction for améliorée. Puis le sens de l'amélioration de l'énoncé est donné par la base for déclaration:

T[] a = Expression;
L1: L2: ... Lm:
for (int i = 0; i < a.length; i++) {
        VariableModifiersopt Type Identifier = a[i];
        Statement
}

un et , je sont générées par le compilateur identificateurs qui sont distincts de tout autres identifiants (générés par le compilateur ou autrement) qui sont dans le champ d'application à l' point où la déclaration enhanced for produire.

Dans votre cas, myMap.keySet() renvoie un sous-type de Iterable de sorte que votre instruction for améliorée est équivalente à l'instruction for de base suivante:

for (Iterator<String> iterator = myMap.keySet().iterator(); iterator.hasNext();) {
   String key = iterator.next();

   System.out.println(key);
   System.out.println(myMap.get(key)); 
}

Et myMap.keySet() ne sont ainsi appelés qu'une seule fois.

7
répondu Pascal Thivent 2010-01-14 03:39:00

Oui, il n'est appelé qu'une seule fois de toute façon

5
répondu James L 2009-05-24 20:33:05

Je crois que c'est un compilateur optimisé pour ne s'exécuter qu'une seule fois par entrée de boucle.

-2
répondu Kris 2009-05-24 20:35:03