Saltar al contenido

¿Cuál es la diferencia entre Collection.stream (). ForEach () y Collection.forEach ()?

Solución:

Para casos simples como el ilustrado, son casi iguales. Sin embargo, hay una serie de diferencias sutiles que pueden ser significativas.

Un problema es el pedido. Con Stream.forEach, el orden es indefinido. Es poco probable que ocurra con transmisiones secuenciales, aún así, está dentro de la especificación para Stream.forEach para ejecutar en algún orden arbitrario. Esto ocurre con frecuencia en corrientes paralelas. Por el contrario, Iterable.forEach siempre se ejecuta en el orden de iteración del Iterable, si se especifica uno.

Otro problema son los efectos secundarios. La acción especificada en Stream.forEach se requiere ser no interfiriendo. (Consulte el documento del paquete java.util.stream). Iterable.forEach potencialmente tiene menos restricciones. Para las colecciones en java.util, Iterable.forEach generalmente usará la colección Iterator, la mayoría de las cuales están diseñadas para ser rápidas y que arrojarán ConcurrentModificationException si la colección se modifica estructuralmente durante la iteración. Sin embargo, las modificaciones que no son estructurales están permitido durante la iteración. Por ejemplo, la documentación de la clase ArrayList dice que “simplemente establecer el valor de un elemento no es una modificación estructural”. Por lo tanto, la acción para ArrayList.forEach se permite establecer valores en el subyacente ArrayList sin problemas.

Las colecciones concurrentes son nuevamente diferentes. En lugar de ser rápidos, están diseñados para ser débilmente consistentes. La definición completa está en ese enlace. Brevemente, sin embargo, considere ConcurrentLinkedDeque. La acción pasó a su forEach método es permitido modificar la deque subyacente, incluso estructuralmente, y ConcurrentModificationException nunca se lanza. Sin embargo, la modificación que se produce puede ser visible o no en esta iteración. (De ahí la consistencia “débil”).

Aún otra diferencia es visible si Iterable.forEach está iterando sobre una colección sincronizada. En tal colección, Iterable.forEach toma el bloqueo de la colección una vez y lo mantiene en todas las llamadas al método de acción. los Stream.forEach call usa el spliterator de la colección, que no se bloquea y que se basa en la regla imperante de no interferencia. La colección que respalda el flujo podría modificarse durante la iteración, y si es así, un ConcurrentModificationException o podría resultar un comportamiento inconsistente.

Esta respuesta se refiere en sí misma al rendimiento de las diversas implementaciones de los bucles. Es solo marginalmente relevante para los bucles que se llaman MUY FRECUENTES (como millones de llamadas). En la mayoría de los casos, el contenido del bucle será, con mucho, el elemento más caro. Para situaciones en las que haces bucles con mucha frecuencia, esto podría resultar de interés.

Debe repetir estas pruebas en el sistema de destino, ya que es una implementación específica (código fuente completo).

Ejecuto openjdk versión 1.8.0_111 en una máquina Linux rápida.

Escribí una prueba que se repite 10 ^ 6 veces en una lista usando este código con diferentes tamaños para integers (10 ^ 0 -> 10 ^ 5 entradas).

Los resultados se muestran a continuación, el método más rápido varía según la cantidad de entradas en la lista.

Pero aún en las peores situaciones, recorrer 10 ^ 5 entradas 10 ^ 6 veces tomó 100 segundos para el peor desempeño, por lo que otras consideraciones son más importantes en prácticamente todas las situaciones.

public int outside = 0;

private void iteratorForEach(List<Integer> integers) {
    integers.forEach((ii) -> {
        outside = ii*ii;
    });
}

private void forEach(List<Integer> integers) {
    for(Integer next : integers) {
        outside = next * next;
    }
}

private void forCounter(List<Integer> integers) {
    for(int ii = 0; ii < integers.size(); ii++) {
        Integer next = integers.get(ii);
        outside = next*next;
    }
}

private void iteratorStream(List<Integer> integers) {
    integers.stream().forEach((ii) -> {
        outside = ii*ii;
    });
}

Aquí están mis tiempos: milisegundos / función / número de entradas en la lista. Cada ejecución es de 10 ^ 6 bucles.

                           1    10    100    1000    10000
       iterator.forEach   27   116    959    8832    88958
               for:each   53   171   1262   11164   111005
         for with index   39   112    920    8577    89212
iterable.stream.forEach  255   324   1030    8519    88419

Si repite el experimento, publiqué el código fuente completo. Edite esta respuesta y agregue los resultados con una notación del sistema probado.


Con una MacBook Pro, Intel Core i7 de 2,5 GHz, 16 GB, macOS 10.12.6:

                           1    10    100    1000    10000
       iterator.forEach   27   106   1047    8516    88044
               for:each   46   143   1182   10548   101925
         for with index   49   145    887    7614    81130
iterable.stream.forEach  393   397   1108    8908    88361

Java 8 Hotspot VM: Intel Xeon de 3,4 GHz, 8 GB, Windows 10 Pro

                            1    10    100    1000    10000
        iterator.forEach   30   115    928    8384    85911
                for:each   40   125   1166   10804   108006
          for with index   30   120    956    8247    81116
 iterable.stream.forEach  260   237   1020    8401    84883

Java 11 Hotspot VM: Intel Xeon de 3,4 GHz, 8 GB, Windows 10 Pro
(la misma máquina que la anterior, diferente versión de JDK)

                            1    10    100    1000    10000
        iterator.forEach   20   104    940    8350    88918
                for:each   50   140    991    8497    89873
          for with index   37   140    945    8646    90402
 iterable.stream.forEach  200   270   1054    8558    87449

Java 11 OpenJ9 VM: Intel Xeon de 3,4 GHz, 8 GB, Windows 10 Pro
(misma máquina y versión de JDK que la anterior, VM diferente)

                            1    10    100    1000    10000
        iterator.forEach  211   475   3499   33631   336108
                for:each  200   375   2793   27249   272590
          for with index  384   467   2718   26036   261408
 iterable.stream.forEach  515   714   3096   26320   262786

Java 8 Hotspot VM: AMD de 2,8 GHz, 64 GB, Windows Server 2016

                            1    10    100    1000    10000
        iterator.forEach   95   192   2076   19269   198519
                for:each  157   224   2492   25466   248494
          for with index  140   368   2084   22294   207092
 iterable.stream.forEach  946   687   2206   21697   238457

Java 11 Hotspot VM: AMD de 2,8 GHz, 64 GB, Windows Server 2016
(la misma máquina que la anterior, diferente versión de JDK)

                            1    10    100    1000    10000
        iterator.forEach   72   269   1972   23157   229445
                for:each  192   376   2114   24389   233544
          for with index  165   424   2123   20853   220356
 iterable.stream.forEach  921   660   2194   23840   204817

Java 11 OpenJ9 VM: AMD de 2,8 GHz, 64 GB, Windows Server 2016
(misma máquina y versión de JDK que la anterior, VM diferente)

                            1    10    100    1000    10000
        iterator.forEach  592   914   7232   59062   529497
                for:each  477  1576  14706  129724  1190001
          for with index  893   838   7265   74045   842927
 iterable.stream.forEach 1359  1782  11869  104427   958584

La implementación de VM que elija también marca la diferencia Hotspot / OpenJ9 / etc.

No hay diferencia entre los dos que ha mencionado, al menos conceptualmente, el Collection.forEach() es solo una taquigrafía.

Internamente el stream() La versión tiene algo más de sobrecarga debido a la creación de objetos, pero en cuanto al tiempo de ejecución, tampoco tiene una sobrecarga allí.

Ambas implementaciones terminan iterando sobre el collection contenido una vez, y durante la iteración imprime el elemento.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *