Me encontré con una pregunta al usar un List
y su stream()
método. 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 única ruta determinada contiene alguna de las rutas especificadas en la lista. Me gustaría devolver un en boolean
función de si se cumplió o no la condición.
Esto, por supuesto, no es una tarea difícil en sí misma. 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: Stream
private boolean isExcluded(String path){
return EXCLUDE_PATHS.stream()
.map(String::toLowerCase)
.filter(path::contains)
.collect(Collectors.toList())
.size() > 0;
}
Ejemplo: bucle para cada uno
private boolean isExcluded(String path){
for (String excludePath : EXCLUDE_PATHS) {
if(path.contains(excludePath.toLowerCase())){
return true;
}
}
return false;
}
Tenga en cuenta que el path
parámetro siempre está en minúsculas .
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 seguiría recorriendo 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?
fuente
new String[]{…}
aquí. Solo useArrays.asList("my/path/one", "my/path/two")
String[]
, no es necesario llamarArrays.asList
. Puede simplemente transmitir sobre la matriz usandoArrays.stream(array)
. Por cierto, tengo dificultades para comprenderisExcluded
por completo el propósito de la prueba. ¿Es realmente interesante si un elemento deEXCLUDE_PATHS
está literalmente contenido en algún lugar dentro de la ruta? Es decirisExcluded("my/path/one/foo/bar/baz")
, volverétrue
, así comoisExcluded("foo/bar/baz/my/path/one/")
…Arrays.stream
método, gracias por señalarlo. De hecho, el ejemplo que publiqué parece bastante inútil para cualquier otra persona además de mí. Soy consciente del comportamiento delisExcluded
método, pero en realidad es algo que necesito para mí, por lo tanto, para responder a su pregunta: sí , es interesante por razones que me gustaría no mencionar, ya que no encajaría en el alcance. de la pregunta original.toLowerCase
aplica a la constante que ya es minúscula? ¿No debería aplicarse alpath
argumento?Respuestas:
Tu suposición es correcta. La implementación de su transmisión es más lenta que el bucle for.
Sin embargo, este uso de transmisión debe ser tan rápido como el bucle for:
Esto itera a través de los elementos, aplicando
String::toLowerCase
un filtro a los elementos uno por uno y terminando en el primer elemento que coincide.Ambos
collect()
&anyMatch()
son operaciones terminales.anyMatch()
sin embargo, sale en el primer elemento encontrado, mientras quecollect()
requiere que se procesen todos los elementos.fuente
findFirst()
en combinación confilter()
. Aparentemente, no sé cómo usar las transmisiones tan bien como pensaba.La decisión de utilizar Streams o no no debe basarse en la consideración del rendimiento, sino más bien en la legibilidad. Cuando se trata realmente de rendimiento, hay otras consideraciones.
Con su
.filter(path::contains).collect(Collectors.toList()).size() > 0
enfoque, está procesando todos los elementos y reuniéndolos en un temporalList
, antes de comparar el tamaño, aún así, esto casi nunca importa para un Stream que consta de dos elementos.El uso
.map(String::toLowerCase).anyMatch(path::contains)
puede ahorrar ciclos de CPU y memoria, si tiene una cantidad sustancialmente mayor de elementos. Aún así, esto convierte cada unoString
a su representación en minúsculas, hasta que se encuentra una coincidencia. Obviamente, tiene sentido usaren 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 elementosEXCLUDE_PATHS
o la longitud de las cadenas se vuelve muy grande, puede considerar usarCompilar una cadena como patrón de expresiones regulares con la
LITERAL
bandera, hace que se comporte como operaciones de cadena ordinarias, pero permite que el motor dedique algún tiempo a la preparación, por ejemplo, utilizando el algoritmo de 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 reales de desempeño, además de la primera pregunta de si esta operación alguna vez será crítica para el desempeño. No es la cuestión de si usar Streams o
for
Loops.Por cierto, los ejemplos de código anteriores mantienen la lógica de su código original, que me parece cuestionable. Su
isExcluded
método devuelvetrue
, si la ruta especificada contiene alguno de los elementos de la lista, por lo que devuelvetrue
para/some/prefix/to/my/path/one
, así comomy/path/one/and/some/suffix
o incluso/some/prefix/to/my/path/one/and/some/suffix
.Incluso
dummy/path/onerous
se considera que cumple los criterios ya quecontains
la cadenamy/path/one
...fuente
Si. Tienes razón. Su enfoque de corriente tendrá algunos gastos generales. Pero puedes usar tal construcción:
La razón principal para usar streams es que hacen que su código sea más simple y fácil de leer.
fuente
anyMatch
un atajo parafilter(...).findFirst().isPresent()
?El objetivo de las secuencias en Java es simplificar la complejidad de escribir código paralelo. Está inspirado en la programación funcional. El flujo en serie es solo para limpiar el código.
Si queremos rendimiento, deberíamos usar paraleloStream, que fue diseñado para. El serial, en general, es más lento.
Hay un buen artículo para leer acerca de , y rendimiento .
ForLoop
Stream
ParallelStream
En su código, podemos utilizar métodos de terminación para detener la búsqueda en la primera coincidencia. (anyMatch ...)
fuente
Como otros han mencionado muchos puntos buenos, solo quiero mencionar la evaluación perezosa en la evaluación de transmisiones. Cuando lo hacemos
map()
para crear una secuencia de rutas en minúsculas, no estamos creando la secuencia completa inmediatamente, sino que la secuencia se construye de forma perezosa , por lo que el rendimiento debería ser equivalente al bucle for tradicional. No está haciendo un escaneo completomap()
yanyMatch()
se ejecutan al mismo tiempo. Una vez queanyMatch()
devuelve verdadero, se cortocircuitará.fuente