streams procesamiento print parte metodos ejemplo datos con collection java java-8 java-stream

procesamiento - stream java 8 ejemplo



¿Cuándo debo usar transmisiones? (4)

El objetivo de las transmisiones en Java es simplificar la complejidad de escribir código paralelo. Está inspirado en la programación funcional. La transmisión en serie es solo para hacer que el código sea más limpio.

Si queremos rendimiento, debemos usar parallelStream, que fue diseñado para. El serial, en general, es más lento.

Hay un buen artículo para leer sobre ForLoop , Stream y ParallelStream Performance .

En su código podemos usar métodos de terminación para detener la búsqueda en la primera coincidencia. (anyMatch ...)

Acabo de encontrar una pregunta cuando uso una List y su método stream() . Si bien sé cómo usarlos, no estoy muy seguro de cuándo usarlos.

Por ejemplo, tengo una lista que contiene varias rutas a diferentes ubicaciones. Ahora, me gustaría comprobar si una sola ruta dada contiene alguna de las rutas especificadas en la lista. Me gustaría devolver un boolean función de si se cumplió o no la condición.

Esto, por supuesto, no es una tarea difícil per se. Pero me pregunto si debería usar streams o un bucle for (-each).

La lista

private static final List<String> EXCLUDE_PATHS = Arrays.asList(new String[]{ "my/path/one", "my/path/two" });

Ejemplo: transmisión

private boolean isExcluded(String path){ return EXCLUDE_PATHS.stream() .map(String::toLowerCase) .filter(path::contains) .collect(Collectors.toList()) .size() > 0; }

Ejemplo: para cada bucle

private boolean isExcluded(String path){ for (String excludePath : EXCLUDE_PATHS) { if(path.contains(excludePath.toLowerCase())){ return true; } } return false; }

Tenga en cuenta que el parámetro de path siempre está en minúscula .

Mi primera suposición es que el enfoque para cada uno es más rápido, porque el ciclo volvería inmediatamente, si se cumple la condición. Mientras que la secuencia todavía se repite en todas las entradas de la lista para completar el filtrado.

¿Es correcta mi suposición? Si es así, ¿ por qué (o más bien cuándo ) usaría stream() entonces?


La decisión de usar Streams o no no debe basarse en la consideración del rendimiento, sino en la legibilidad. Cuando realmente se trata de rendimiento, hay otras consideraciones.

Con su .filter(path::contains).collect(Collectors.toList()).size() > 0 , está procesando todos los elementos y .filter(path::contains).collect(Collectors.toList()).size() > 0 en una List temporal, antes de comparar el tamaño, aún así, esto casi nunca Importa para una secuencia que consta de dos elementos.

El uso de .map(String::toLowerCase).anyMatch(path::contains) puede ahorrar ciclos de CPU y memoria, si tiene un número de elementos sustancialmente mayor. Aún así, esto convierte cada String en su representación en minúsculas, hasta que se encuentre una coincidencia. Obviamente, hay un punto en usar

private static final List<String> EXCLUDE_PATHS = Stream.of("my/path/one", "my/path/two").map(String::toLowerCase) .collect(Collectors.toList()); private boolean isExcluded(String path) { return EXCLUDE_PATHS.stream().anyMatch(path::contains); }

en lugar. Por lo tanto, no tiene que repetir la conversión a minúsculas en cada invocación de isExcluded . Si el número de elementos en EXCLUDE_PATHS o la longitud de las cadenas es realmente grande, puede considerar usar

private static final List<Predicate<String>> EXCLUDE_PATHS = Stream.of("my/path/one", "my/path/two").map(String::toLowerCase) .map(s -> Pattern.compile(s, Pattern.LITERAL).asPredicate()) .collect(Collectors.toList()); private boolean isExcluded(String path){ return EXCLUDE_PATHS.stream().anyMatch(p -> p.test(path)); }

La compilación de una cadena como patrón de expresiones regulares con el indicador LITERAL hace que se comporte como las operaciones de cadena ordinarias, pero permite que el motor dedique un tiempo a la preparación, por ejemplo, utilizando el algoritmo Boyer Moore, para ser más eficiente cuando se trata de la comparación real.

Por supuesto, esto solo vale la pena si hay suficientes pruebas posteriores para compensar el tiempo dedicado a la preparación. Determinar si este será el caso, es una de las consideraciones de rendimiento reales, además de la primera pregunta de si esta operación será crítica para el rendimiento. No es la pregunta si usar Streams o for bucles.

Por cierto, los ejemplos de código anteriores mantienen la lógica de su código original, que me parece cuestionable. Su método isExcluded devuelve true , si la ruta especificada contiene alguno de los elementos de la lista, por lo que devuelve true para /some/prefix/to/my/path/one , así como my/path/one/and/some/suffix o incluso /some/prefix/to/my/path/one/and/some/suffix .

Incluso dummy/path/onerous se considera que cumple con los criterios, ya que contains la cadena my/path/one ...


Sí. Tienes razón. Su enfoque de transmisión tendrá algunos gastos generales. Pero puede usar tal construcción:

private boolean isExcluded(String path) { return EXCLUDE_PATHS.stream().map(String::toLowerCase).anyMatch(path::contains); }

La razón principal para usar transmisiones es que hacen que su código sea más simple y fácil de leer.


Tu suposición es correcta. La implementación de la transmisión es más lenta que el ciclo for.

Sin embargo, este uso de la transmisión debería ser tan rápido como el ciclo for:

EXCLUDE_PATHS.stream() .map(String::toLowerCase) .anyMatch(path::contains);

Esto itera a través de los elementos, aplicando String::toLowerCase y el filtro a los elementos uno por uno y terminando en el primer elemento que coincida.

Tanto collect() anyMatch() son operaciones de terminal. anyMatch() sale del primer elemento encontrado, mientras que collect() requiere que se procesen todos los elementos.