tutorial lambdas expressions example java lambda java-8

java - lambdas - ¿Cuál sería mejor en términos de rendimiento Lambda o simple loop?



lambda expressions (9)

¿Por qué debería usar esto en lugar de un método real?

Usted no debe. Utiliza el enfoque que más te guste.

En cuanto al rendimiento, supongo, todas estas versiones son más o menos igualmente rápidas. Aquí la operación de E / S ( println ) es mucho más lenta que todas las sobrecargas posibles al llamar a lambda o al crear un iterador. En general, forEach podría ser un poco más rápido, ya que hace todo dentro del único método sin crear el Iterator y llamar a los hasNext y a los next (lo cual se realiza implícitamente para cada bucle). De todos modos, esto depende de muchos factores, como la frecuencia con que se llama a este código, la duración de la lista, si el compilador JIT logró desvirtualizar el iterador y / o lambda, y así sucesivamente.

Esta pregunta ya tiene una respuesta aquí:

He leído rápidamente la documentación de Oracle Lambda Expression .

Este tipo de ejemplo me ha ayudado a entender mejor, sin embargo:

//Old way: List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7); for(Integer n: list) { System.out.println(n); } //New way: List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7); list.forEach(n -> System.out.println(n)); //or we can use :: double colon operator in Java 8 list.forEach(System.out::println);

Aún así, no entiendo por qué es una innovación. Es solo un método que muere cuando la "variable de método" termina, ¿verdad? ¿Por qué debería usar esto en lugar de un método real? Cuál sería la mejor opción en términos de rendimiento. Lambda o lazo simple.


Cuando usa lambda en su código, está diciendo qué hacer y no cómo hacerlo. Por encima del código que pasa una referencia de método en lugar de pasar por encima de la lista, creo que el lambda es más ergonómico.

En lo que respecta al rendimiento, la impresión de una lista de 1 millón de elementos tomó casi el mismo tiempo, pero no he marcado otro tipo de operación.

El código anterior es una operación trivial pero lambda tiene bastantes ventajas ya que puede tener funciones que puede pasar, utilizando múltiples núcleos (flujos paralelos) es fácil, etc.


El tiempo de ejecución y la legibilidad ciertamente varían según el caso, pero en mi opinión hay una respuesta bastante buena para

¿Por qué debería usar esto en lugar de un método real?

Puede pasar una función lambda como variable
Corrígeme si estoy fuera de base en esto, ya que he sido principalmente un usuario Fantom por un tiempo, pero puedes pasar una función Lambda como argumento a una llamada a método. Por ejemplo, si tiene un método de clasificación creado, podría tomar un argumento lambda como el comparador a usar, lo que le permite usar el único método de clasificación para comparar diferentes campos en un objeto, o incluso objetos completamente diferentes, fácilmente.


En términos de rendimiento, una función normal será mejor que la lambda porque en groovy hay cierres presentes que son más o iguales a lambda.

Estas cosas funcionan de forma similar a como si escribieras un cierre para cualquier colección, crearía internamente otra clase que realmente actuara para el cierre mencionado o la expresión lambda.

Pero, al usar lambda y cierre, puedo iterar cosas de una mejor manera, así como también puedo depurar fácilmente. Puedes escribir menos líneas de códigos.


Le permite escribir en una línea (mientras tiene suficiente legibilidad) algo, lo que antes no era posible. El rendimiento no es un problema aquí O(n) mantiene O(n) .

Por ejemplo, en mi aplicación para Windows Phone, tengo sesiones y en esas sesiones hay artistas y quiero seleccionar todas las sesiones que tienen un actor concreto (como si quisieran ver todas las películas en las que juega un actor). En Java 1.7 o menos, tuve que crear un bucle con un bucle interno, verificándolo, devolviéndolo nulo si no había ningún ejecutante, etc. Y con expresiones lambda, puedo hacer esto:

//performerId is parameter passed by method before Sessions.Where(x => x.Performers.Where(y => y.PerformerId == performerId).FirstOrDefault() != null)

Es lo mismo en Java ahora (sin embargo, no estoy trabajando en el proyecto 1.8 en este momento, no tengo el ejemplo de Java, pero lo estoy esperando).


Mi consejo sería:

  1. usar el estilo que usted y sus compañeros de trabajo acuerden es más fácil de mantener,

  2. si usted / sus colegas aún no se sienten cómodos con lambdas, siga aprendiendo

  3. no te obsesiones con el rendimiento

En términos generales, lambdas y streams proporcionan una forma más concisa y (una vez que todo el mundo esté a la altura) más legible de expresar este tipo de algoritmo. El rendimiento no es el objetivo principal.

Si el rendimiento se convierte en un problema, entonces el consejo estándar es codificar, probar, perfilar, optimizar. Puede perder tiempo fácilmente optimizando en la etapa de codificación. Permita que el perfilador señale qué partes de su código son dignas del esfuerzo de optimización.

En este ejemplo específico, la diferencia de rendimiento será demasiado pequeña para medir. Y si amplió a una lista de millones de elementos, el rendimiento estará dominado por el tiempo necesario para crear la lista y escribir los números. Las diferentes formas de iteración solo contribuirán en una pequeña parte al rendimiento general.

Y para la gente, que (a pesar de todo lo anterior) aún desea saber si es más rápido usar un lambda o un bucle convencional, la mejor respuesta general es:

"Depende de todo tipo de factores que 1) no se entiendan bien, y 2) puedan cambiar a medida que evolucione la tecnología del compilador Java.

Podríamos darle una respuesta para un ejemplo específico con un lanzamiento específico de Java mayor / menor / parche, pero no sería prudente generalizar.


Mira la primera parte en la documentación de Guava . Se trata de la versión anterior de Java, pero hace un punto importante: usar lambdas en todas partes podría hacer que el código sea menos legible. Un mejor ejemplo podría ser el de Stream API:

// Group employees by department Map<Department, List<Employee>> byDept = employees.stream() .collect(Collectors.groupingBy(Employee::getDepartment));


Si desea comprender el valor de las expresiones lambda, no debe mirar el único método nuevo que tiene una contraparte de idioma que existía antes. ¿Qué tal estos ejemplos?

button.addActionListener( ev -> BACKGROUND_EXECUTOR_SERVICE.execute( () -> { String result = longComputation(); SwingUtilities.invokeLater( () -> label.setText(result) ); });

Solo piense en cómo se ve el código pre-Java 8 equivalente y verá que la principal ventaja no es el rendimiento aquí.

Si desea ver las operaciones de Cobro, ¿qué tal este?

map.merge(key, 1, Integer::sum);

Esto pondrá 1 en el mapa, si la clave aún no existe en el mapa o agrega 1 al valor de la asignación existente de lo contrario. Nuevamente, piense cómo se ve la contraparte anterior. El hecho de que podría ser aún más eficiente ya que evita múltiples operaciones de hash, si el mapa tiene una implementación apropiada del método, es solo un beneficio adicional.

Sin duda, el método forEach no puede proporcionar una ganancia tan grande en la expresividad, ya que existe el equivalente de cada idioma. Aún así, no necesitar declarar una variable de bucle puede mejorar el código fuente, si la declaración requiere repetir un nombre de tipo largo y parámetros genéricos. Eso es especialmente cierto en el contexto de Map s:

Map<ContainerOrderFocusTraversalPolicy, List<AttributedCharacterIterator>> map; //…

map.forEach((p,l)->l.forEach(i->{ /* do your work using p and i */}));

aquí, esta nueva iteración gana claramente

for(Map.Entry<ContainerOrderFocusTraversalPolicy, List<AttributedCharacterIterator>> e: map.entrySet()) { ContainerOrderFocusTraversalPolicy p=e.getKey(); for(AttributedCharacterIterator i: e.getValue()) { /* do your work using p and i */ } }

Por supuesto, solo importa si las declaraciones de trabajo reales son lo suficientemente pequeñas, pero así es como deberían usarse las expresiones lambda: para encapsular pequeñas partes de código. Y todavía hay tareas que no se pueden hacer de esta manera y requieren un ciclo ordinario para cada ciclo, del mismo modo que también hay tareas que no se pueden hacer con un bucle for-each y necesitan el ciclo for bucle más antiguo for un Iterator manualmente ...


import java.util.Arrays; import java.util.List; import java.util.concurrent.TimeUnit; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Timeout; import org.openjdk.jmh.annotations.Warmup; /** * * @author devb */ @BenchmarkMode(Mode.Throughput) @Fork(value = 1) @Warmup(iterations = 1, time = 32, timeUnit = TimeUnit.MILLISECONDS) @Measurement(iterations = 16, time = 1, timeUnit = TimeUnit.SECONDS) @Timeout(time = 1, timeUnit = TimeUnit.SECONDS) @State(Scope.Benchmark) public class CheckLamdaPerformance { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7); @Benchmark public void testPerOld() { //Old way: for (Integer n : list) { System.out.println(n); } } @Benchmark public void testPerNew() { //New way: list.forEach(n -> System.out.println(n)); } @Benchmark public void testPerDoubleColon() { //or we can use :: double colon operator in Java 8 list.forEach(System.out::println); } }

Para probar el punto de referencia

import java.io.IOException; import org.openjdk.jmh.Main; import org.openjdk.jmh.runner.RunnerException; /** * * @author devb */ public class MyBenchmark { private static final String TEST = ".*CheckLamdaPerformance.*"; public static void main(String[] args) throws IOException, RunnerException { Main.main(getArguments(TEST, 1, 5000, 1)); } private static String[] getArguments(String className, int nRuns, int runForMilliseconds, int nThreads) { return new String[]{className, "-i", "" + nRuns, "-r", runForMilliseconds + "ms", "-t", "" + nThreads, "-w", "5000ms", "-wi", "1" }; } }

Después de ejecutar la salida es:

# Run complete. Total time: 00:00:34 Benchmark Mode Cnt Score Error Units CheckLamdaPerformance.testPerDoubleColon thrpt 776.008 ops/s CheckLamdaPerformance.testPerNew thrpt 1096.423 ops/s CheckLamdaPerformance.testPerOld thrpt 873.542 ops/s