2009-05-24 9 views
22

tengo algo como esto:Java eficiencia foreach

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

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

Así es myMap.keySet() llamada una vez en el bucle foreach ? Creo que sí, pero quiere su opinión.

me gustaría saber si el uso de foreach de esta manera (myMap.keySet()) tiene un impacto en el rendimiento o que es equivalente a esto:

Set<String> keySet = myMap.keySet(); 
for (String key : keySet) { 
    ... 
} 
+0

(La sintaxis del bucle for mejorado está un poco atrás.) –

+2

No sé si estaría de acuerdo con llamar a esta optimización prematura. Es razonable querer entender qué está haciendo el compilador con su código. Tampoco tenemos idea de en qué punto de su proyecto (si está trabajando en un proyecto y no preguntando académicamente) lo está pidiendo. Podría ser al final. –

+0

+1 para la pregunta. – user12458

Respuesta

65

Si quiero estar absolutamente seguro, luego compilarlo en ambos sentidos y descompilarlo y comparar. Lo hice con la siguiente fuente:

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)); 
    } 
} 

y cuando descompilar el archivo de clase con Jad, me sale:

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); 
    } 
} 

Así que ahí tienes tu respuesta. Se llama una vez con forma for-loop.

+10

+1 para una prueba real de lo que hace el compilador –

+0

Woah! eso es interesante ... Agradable. +1. –

+0

Perdón por el comentario negativo, pero algo parece extraño aquí. ¿Por qué es una instrucción println en la sección final del for-loop, pero la llamada al siguiente está en el cuerpo? Parece una configuración muy extraña. – Carcigenicate

5

Sí, se llama sólo una vez en ambos sentidos

-2

Creo que es un compilador optimizado para ejecutarse solo una vez por entrada de bucle.

9

keySet() se llama solo una vez. El "bucle forzado mejorado" se basa en la interfaz Iterable, que utiliza para obtener un Iterator, que luego se utiliza para el bucle. Ni siquiera es posible repetir un Set de otra manera, ya que no hay ningún índice ni nada por el cual pueda obtener elementos individuales.

Sin embargo, lo que realmente debe hacer es abandonar por completo este tipo de problemas de micro-optimización: si alguna vez tiene problemas reales de rendimiento, existe la probabilidad de que sea algo que nunca había pensado por su cuenta.

+2

"lo que realmente debería hacer es abandonar por completo este tipo de preocupaciones de micro-optimización" La preocupación que tenía habría sido difícilmente una micro-optimización en general ... – hhafez

+0

Puede construir un caso especial malicioso que resulte en un gran problema de rendimiento para casi cualquier cosa, pero eso no cambia el hecho de que, con seguridad, no habría sido un problema. Por un lado, los conjuntos de claves están almacenados en caché en cualquier implementación de Map que haya visto. –

+0

Micro optimización - ¡me importa! ¡Ellos hacen la diferencia en programas complejos con seguridad! – user12458

35

Se llama solo una vez. De hecho, usa un iterador para hacer el truco.

Por otra parte, en su caso, creo que se debe utilizar

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

a evitar la búsqueda en el mapa cada vez.

+2

¡Gracias a todos por compartir su sabiduría! ¡Ojalá mis compañeros fueran iguales! –

+0

¿Qué sucede si myMap.entrySet() no devuelve un valor constante (por ejemplo, myMap se actualiza dentro del ciclo como si se agregara un par clave-valor)? ¿Se llama solo una vez? ¿No produciría eso resultados extraños? – user12458

+0

@JavaTechnical: la cosa es que no debes cambiar el contenido del mapa mientras lo actualizas; de lo contrario, obtienes una ConcurrentModificationException. Si necesita cambiar el mapa mientras itera, la única forma segura de hacerlo es a través de un iterador. –

7

La respuesta está en la especificación del lenguaje Java, no necesita descompilar :) Esto es lo que podemos leer sobre the enhanced for statement:

La mejorada de instrucción tiene la forma :

EnhancedForStatement: 
     for (VariableModifiersopt Type Identifier: Expression) Statement 

La expresión debe tener el tipo Iterable o debe ser de un tipo de matriz (§10.1), o se produce un error en tiempo de compilación.

El alcance de una variable local declarada en la parte FormalParameter de un mejorado for declaración (§14.14) es la declaración contenida

El significado de la declaración mejorada for está dada por la traducción a un for declaración básica.

Si el tipo de Expression es un subtipo de Iterable, a continuación, dejar que I ser el tipo de la expresión Expresión.iterator(). La mejorada for declaración es equivalente a un for declaración básica de la forma :

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

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

Dónde #i es una identificador generado por el compilador que es distinta de cualquier otro identificadores (compilador generado o de otra manera) que están dentro del alcance (§6.3) en el punto en que ocurre la declaración mejorada para .

lo contrario, la expresión necesariamente tiene un tipo de matriz, T[]. Deje que L1 ... Lm sea la secuencia (posiblemente vacía) de las etiquetas que preceden inmediatamente a la declaración mejorada de for. Entonces el significado de la mejorada para declaración está dada por la siguiente declaración básica for :

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

Dónde un y i son generados por el compilador identificadores que son distintos de cualquier otro identificadores (generado por el compilador u otro) que están en el alcance en el punto donde se produce la sentencia mejorada para .

En su caso myMap.keySet(), devuelve un subtipo de Iterable por lo que su mayor for afirmación es equivalente a la siguiente for declaración básica:

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

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

Y myMap.keySet() se llama así sólo una vez.