En muchos otros idiomas, p. Ej. Haskell, es fácil repetir un valor o una función varias veces, p. Ej. para obtener una lista de 8 copias del valor 1:
take 8 (repeat 1)
pero no he encontrado esto todavía en Java 8. ¿Existe tal función en el JDK de Java 8?
O alternativamente algo equivalente a un rango como
[1..8]
Parecería un reemplazo obvio para una declaración detallada en Java como
for (int i = 1; i <= 8; i++) {
System.out.println(i);
}
tener algo como
Range.from(1, 8).forEach(i -> System.out.println(i))
aunque este ejemplo en particular no parece mucho más conciso en realidad ... pero con suerte es más legible.
Respuestas:
Para este ejemplo específico, puede hacer:
Si necesita un paso diferente de 1, puede utilizar una función de mapeo, por ejemplo, para un paso de 2:
O cree una iteración personalizada y limite el tamaño de la iteración:
fuente
IntStream.rangeClosed(1, 8).forEach(i -> methodNoArgs());
) pero confunde algo en mi opinión y en ese caso parece indicado un bucle.Aquí hay otra técnica que encontré el otro día:
los
Collections.nCopies
llamada crea unList
contenido den
copias de cualquier valor que proporcione. En este caso, es elInteger
valor en caja 1. Por supuesto, en realidad no crea una lista conn
elementos; crea una lista "virtualizada" que contiene solo el valor y la longitud, y cualquier llamada aget
dentro del rango solo devuelve el valor. ElnCopies
método ha existido desde que se introdujo el marco de colecciones en JDK 1.2. Por supuesto, la capacidad de crear una secuencia a partir de su resultado se agregó en Java SE 8.Gran cosa, otra forma de hacer lo mismo en aproximadamente el mismo número de líneas.
Sin embargo, esta técnica es más rápido que el
IntStream.generate
yIntStream.iterate
se acerca, y sorprendentemente, también es más rápido que elIntStream.range
enfoque.por
iterate
ygenerate
el resultado quizás no sea demasiado sorprendente. El marco de transmisiones (en realidad, los divisores de estas transmisiones) se basa en la suposición de que las lambdas generarán potencialmente diferentes valores cada vez, y que generarán un número ilimitado de resultados. Esto hace que la división en paralelo sea particularmente difícil. Eliterate
método también es problemático para este caso porque cada llamada requiere el resultado de la anterior. Entonces, los flujos que usangenerate
yiterate
no funcionan muy bien para generar constantes repetidas.El rendimiento relativamente bajo de
range
es sorprendente. Esto también está virtualizado, por lo que no todos los elementos existen en la memoria, y el tamaño se conoce de antemano. Esto debería hacer un spliterator rápido y fácilmente paralelizable. Pero sorprendentemente no le fue muy bien. Quizás la razón es querange
tiene que calcular un valor para cada elemento del rango y luego llamar a una función sobre él. Pero esta función simplemente ignora su entrada y devuelve una constante, por lo que me sorprende que esto no esté alineado y eliminado.La
Collections.nCopies
técnica tiene que hacer boxing / unboxing para poder manejar los valores, ya que no existen especializaciones primitivas deList
. Dado que el valor es el mismo cada vez, básicamente está encuadrado una vez y ese cuadro es compartido por todas lasn
copias. Sospecho que el boxeo / unboxing está altamente optimizado, incluso intrinsificado, y puede insertarse bien.Aquí está el código:
Y aquí están los resultados de JMH: (2.8GHz Core2Duo)
Hay una buena cantidad de variación en la versión ncopies, pero en general parece cómodamente 20 veces más rápida que la versión de rango. (Sin embargo, estaría dispuesto a creer que hice algo mal).
Me sorprende lo bien que funciona la
nCopies
técnica. Internamente, no es muy especial, ya que el flujo de la lista virtualizada simplemente se implementa usandoIntStream.range
! Esperaba que fuera necesario crear un spliterator especializado para que esto fuera rápido, pero ya parece bastante bueno.fuente
nCopies
realidad no copia nada y que todas las "copias" apuntan a ese único objeto. Siempre es seguro si ese objeto es inmutable , como una primitiva en caja en este ejemplo. Usted hace alusión a esto en su declaración "encajonado una vez", pero sería bueno mencionar explícitamente las advertencias aquí porque ese comportamiento no es específico del auto-boxing.LongStream.range
es significativamente más lento queIntStream.range
? Así que es bueno que se haya descartado la idea de no ofrecer unIntStream
(pero usarloLongStream
para todos los tipos de enteros). Tenga en cuenta que para el caso de uso secuencial, no hay ninguna razón para usar la transmisión en absoluto:Collections.nCopies(8, 1).forEach(i -> System.out.println(i));
hace lo mismo,Collections.nCopies(8, 1).stream().forEach(i -> System.out.println(i));
pero podría ser incluso más eficienteCollections.<Runnable>nCopies(8, () -> System.out.println(1)).forEach(Runnable::run);
LongStream.range
funcione peor, porque tiene dos mapas con elLongFunction
interior, mientras quencopies
tiene tres mapas conIntFunction
,ToLongFunction
yLongFunction
, por lo tanto, todas las lambdas son monomórficas. La ejecución de esta prueba en un perfil de tipo precontaminado (que está más cerca del caso del mundo real) muestra quencopies
es 1,5 veces más lento.for
ciclo antiguo simple . Si bien su solución es más rápida que elStream
código, supongo que unfor
bucle superaría a cualquiera de estos por un margen significativo.Para completar, y también porque no pude evitarlo :)
Generar una secuencia limitada de constantes está bastante cerca de lo que vería en Haskell, solo con nivel de detalle de Java.
fuente
() -> 1
solo generaría 1, ¿está previsto? Entonces la salida sería1 1 1 1 1 1 1 1
.take 8 (repeat 1)
. Assylias cubrió prácticamente todos los demás casos.Stream<T>
también tiene ungenerate
método genérico para obtener un flujo infinito de algún otro tipo, que puede limitarse de la misma manera.Una vez que una función de repetición se define en algún lugar como
Puede usarlo de vez en cuando de esta manera, por ejemplo:
Para obtener y equivalente a Haskell
Podrías escribir
fuente
Runnable
aFunction<Integer, ?>
y luego utilizandof.apply(i)
.Esta es mi solución para implementar la función de tiempos. Soy un junior, así que admito que podría no ser ideal, me alegraría saber si esto no es una buena idea por cualquier motivo.
Aquí hay un ejemplo de uso:
fuente