2010-05-12 22 views
12

decir supongamos que tengo clase como:Llamar a un getter varias veces o llamar una vez y asignar a una variable?

public class Age { 

    private int age; 

    public int getAge() { 
     return this.age; 
    } 

} 

En mi clase principal Estoy llamando a los getAge() método muchas veces.

Así que quería saber si es aconsejable llamar tantas veces o llamar una vez y asignarle una variable y usar esa variable.

cuál es el mejor y por qué?

+0

Un tema bastante similar (aunque relacionado con C++) fue discutido recientemente en SO, ver http://stackoverflow.com/questions/2753381/ - quizás algunas de las respuestas expresadas allí también sean útiles para usted. – stakx

Respuesta

0

Para un caso simple como este, elegiría el que se vea mejor en cuanto a código.

Hay algunos casos en los que es recomendable llamar una vez y leer el valor de retorno salvado, como por ejemplo en

for (int i = 0; i < list.size(); i++) 
    doSomethingThatDoesNotAffectSizeOfList(); 

ya que el compilador puede tener problemas para averiguar si el cuerpo del bucle afecta al tamaño de la lista. Una lista se implementa correctamente siempre debe ser capaz de decirle a su tamaño con facilidad, pero en otros ejemplos (o cuando se trata de colecciones mal implementados) que podría ser peor.

En general, si un método es computacionalmente pesado, puede usar memoization, lo que básicamente significa que almacena en caché los valores de entrada/salida ya computados.

Una función memorizada "recuerda" los resultados correspondientes a un conjunto de entradas específicas. Las llamadas posteriores con entradas recordadas devolver el resultado recordado que en vez de recalcular, eliminando así el coste principal de una llamada con los parámetros dados de todo pero la primera llamada realizada a la función con esos parámetros.

Esta técnica empuja el "salvar-la-vuelta-relación calidad-eficiencia" en el método en sí, lo que facilita el mantenimiento bla bla bla ...

+0

eso es lo que estoy preguntando aquí, ¿cuál y por qué? – GuruKulki

+0

@GK: si al llamar 'getAge()' no afecta el tamaño de la lista, probablemente no importe. A menos que (como alguien más mencionado aquí) el método 'getAge()' realice algunos cálculos de larga duración. Si 'getAge()' solo devuelve el valor de una variable privada, puede suponer que no es un "cálculo de larga ejecución". – FrustratedWithFormsDesigner

0

creo que no verá la diferencia en tiempo de ejecución, suponiendo que no cree más de una clase de edad.

7

No intente optimizar al mínimo esto, a menos que descubra que es realmente un cuello de botella mientras crea perfiles. Utilizaría el método de acceso getAge(), ya que probablemente sea la solución más fácil de mantener.

Dicho esto, es probable que los dos enfoques funcionen exactamente igual. En tiempo de ejecución, el JIT lo más probable es optimizar la distancia getAge() llamar en su totalidad, por lo que será un único acceso primitivo en ambos casos.

+2

El JIT realmente convertirá una llamada a método en acceso variable directo (¿privado? No conozco el JIT en absoluto, así que no estoy discutiendo, pero eso es ciertamente sorprendente para mí. –

+2

@Adam: depende de la plataforma, etc., pero el Java JIT, en mi opinión, incluye llamadas a métodos simples que simplemente devuelven una propiedad (como esta) en muchos casos. (Es la optimización equivalente como una propiedad obtener optimización JIT en línea en .NET CLR, por cierto ...) –

10

Es probable que esta sea una situación en la que esté optimizando antes de darse cuenta de que lo necesita. El valor es solo un número entero, por lo que no ocupará mucha memoria si almacena el valor en varios lugares. Al mismo tiempo, es una llamada a método muy simple que no llevará mucho tiempo ejecutar. Escríbelo de una manera que sientas que es más legible. Luego, después de tener una versión sólida del código, puede usar una herramienta de creación de perfiles para ver si hay una diferencia notable.

3

Esto es algo que usted, como escritor de la API, debe indicar a la persona que llama.

En general, si simplemente está devolviendo una propiedad, puede marcar la llamada como una final (si no está ofreciendo una interfaz real). Eso debería reducir los costos de las llamadas ya que el compilador sería más propenso a alinear la función.

Si el costo de calcular la propiedad es costoso (por ejemplo, una búsqueda de cadena), documentarlo en los JAvaDocs para ese método e indicarle a la persona que llama que puede querer obtener el valor una vez y almacenarlo en caché.

+0

+1 para la sugerencia "final" - ("método" sería mejor que "llamar", sin embargo) – leonbloy

2

No te molestes. No vale la pena micro-optimizar así. Espere hasta que termine su código, luego se ejecuta demasiado lento, luego saque un generador de perfiles y trabaje en lo que el generador de perfiles le dice que es el origen del problema.

La optimización prematura es la raíz de todos los males.

1

Trataré de decir con ejemplos de código lo que la otra respuesta ya dijo.

En el caso que presentó la petición de getAge() es muy muy simple, y el costo de la llamada es casi nada. No te molestes con esto en este caso.

Pero si su getAge era algo elegante que hacer un montón de cálculos o de acceso a los recursos IO, como:

public int getAge() { 
    return slowService.calculateAgeByBirthDate(birthDate); // it takes 2 seconds to execute for every call 
} 

entonces seguro que sería una buena idea caché de resultado y usarlo. Porque si lo llamas 30 veces, tu código tardará 1 minuto en completarse.

1

Dependiendo de cómo esté diseñada su aplicación, ¡las dos opciones podrían dar resultados diferentes! Si la instancia de edad que está utilizando es compartida y mutable, diferentes lugares en su aplicación pueden cambiar su valor entre sus llamadas al getAge(). En este caso, es una cuestión de corrección decidir cuál es la mejor opción para su código, y depende de usted decidir. Como dice el viejo adagio: "primero hazlo bien, luego hazlo rápido". Y, como ya han mencionado otros, probablemente no tenga que preocuparse por la parte "hacerlo rápido" en este caso.

Un ejemplo relacionado es cuando se está cambiando una colección, mientras que la iteración a través de él. Debe iterar sobre una instantánea para no obtener un ConcurrentModificationException.

0

La parte difícil es entender que la moderna JVM a hacer optimizaciones agresivas al compilar el código de bytes basada en el conocimiento disponible en tiempo de ejecución.

Si, por ejemplo, un método dado no se reemplaza en una subclase, se puede tratar exactamente igual que un método final, permitiendo a la JVM alinear una copia de su código en el método de llamada en lugar de hacerlo explícitamente llamada. (Si las condiciones cambian, esas clases simplemente se consideran nuevas y, por lo tanto, se recopilan posteriormente según las nuevas condiciones).

Esto significa que get/set para bean attibutes (donde los valores simplemente se almacenan y se recuperan, y no se calculan) son muy baratos y debe hacer las llamadas cada vez y esperar que la JVM detecte las posibles optimizaciones y las aplique.

0

Usted no va a ver mucho cambio en el rendimiento a menos que si usted está haciendo muchas operaciones dentro del método getAge().

0

En este caso, sugeriría que no se ven en el rendimiento, mirar a la facilidad de uso y la reutilización de código. Su implementación actual es la más simple de getters, una que devuelve un entero.

Pero, ¿qué pasa si en algún momento almacena la fecha de nacimiento de una persona y desea generar dinámicamente la edad? Si simplemente llama a la propiedad directamente, entonces se verá forzado a refactorizar su código. Sin embargo, al alterar las partes internas de getAge(), puedes poner el cálculo allí, y listo.

Me gustaría que los lenguajes de programación introduzcan un modificador de propiedad/campo 'super-privado', uno que básicamente diga 'Solo se puede acceder a esta propiedad a través de su acceso'.

Cuestiones relacionadas