Em muitas outras línguas, por exemplo. Haskell, é fácil repetir um valor ou função várias vezes, por exemplo. para obter uma lista de 8 cópias do valor 1:
take 8 (repeat 1)
mas ainda não encontrei isso no Java 8. Essa função existe no JDK do Java 8?
Ou, alternativamente, algo equivalente a um intervalo como
[1..8]
Pareceria um substituto óbvio para uma instrução detalhada em Java como
for (int i = 1; i <= 8; i++) {
System.out.println(i);
}
ter algo como
Range.from(1, 8).forEach(i -> System.out.println(i))
embora este exemplo particular não pareça muito mais conciso na verdade ... mas espero que seja mais legível.
Respostas:
Para este exemplo específico, você poderia fazer:
Se precisar de uma etapa diferente de 1, você pode usar uma função de mapeamento, por exemplo, para uma etapa de 2:
Ou crie uma iteração personalizada e limite o tamanho da iteração:
fonte
IntStream.rangeClosed(1, 8).forEach(i -> methodNoArgs());
) mas confunde IMO e nesse caso um loop parece indicado.Aqui está outra técnica que usei outro dia:
A
Collections.nCopies
chamada cria umList
contendon
cópias de qualquer valor que você fornece. Nesse caso, é oInteger
valor 1 na caixa . É claro que, na verdade, ele não cria uma lista comn
elementos; ele cria uma lista "virtualizada" que contém apenas o valor e o comprimento, e qualquer chamada paraget
dentro do intervalo apenas retorna o valor. onCopies
método existe desde que o Collections Framework foi introduzido no JDK 1.2. Obviamente, a capacidade de criar um fluxo a partir de seu resultado foi adicionada ao Java SE 8.Grande coisa, outra maneira de fazer a mesma coisa com aproximadamente o mesmo número de linhas.
No entanto, esta técnica é mais rápido do que o
IntStream.generate
eIntStream.iterate
se aproxima e, surpreendentemente, é também mais rápido do que aIntStream.range
abordagem.Pois
iterate
egenerate
o resultado talvez não seja muito surpreendente. A estrutura de fluxos (na verdade, os divisores para esses fluxos) é construída na suposição de que os lambdas irão potencialmente gerar valores diferentes a cada vez e que eles irão gerar um número ilimitado de resultados. Isso torna a divisão paralela particularmente difícil. Oiterate
método também é problemático para este caso porque cada chamada requer o resultado da anterior. Portanto, os fluxos usandogenerate
eiterate
não funcionam muito bem para gerar constantes repetidas.O desempenho relativamente baixo de
range
é surpreendente. Isso também é virtualizado, de modo que nem todos os elementos existem realmente na memória e o tamanho é conhecido desde o início. Isso deve resultar em um divisor de fácil paralelização rápido e fácil. Mas, surpreendentemente, não foi muito bem. Talvez a razão seja querange
tem que calcular um valor para cada elemento do intervalo e então chamar uma função sobre ele. Mas essa função simplesmente ignora sua entrada e retorna uma constante, então estou surpreso que isso não seja embutido e eliminado.A
Collections.nCopies
técnica tem que fazer boxing / unboxing para lidar com os valores, já que não existem especializações primitivas deList
. Como o valor é o mesmo todas as vezes, é basicamente embalado uma vez e essa caixa é compartilhada por todas asn
cópias. Eu suspeito que o boxing / unboxing é altamente otimizado, até mesmo intrinsecado, e pode ser bem embutido.Aqui está o código:
E aqui estão os resultados do JMH: (2.8 GHz Core2Duo)
Existe uma grande variação na versão ncopies, mas no geral parece confortavelmente 20x mais rápida do que a versão range. (Eu estaria bastante disposto a acreditar que fiz algo errado, no entanto.)
Estou surpreso com o quão bem a
nCopies
técnica funciona. Internamente, ele não faz muita coisa especial, com o fluxo da lista virtualizada simplesmente sendo implementado usandoIntStream.range
! Eu esperava que fosse necessário criar um divisor especializado para fazer isso funcionar rápido, mas já parece estar muito bom.fonte
nCopies
nada realmente copia e todas as "cópias" apontam para aquele único objeto. É sempre seguro se esse objeto for imutável , como um primitivo em caixa neste exemplo. Você alude a isso em sua declaração "boxed once", mas pode ser bom chamar explicitamente as advertências aqui, porque esse comportamento não é específico para a caixa automática.LongStream.range
significa que é significativamente mais lento do queIntStream.range
? Portanto, é uma boa coisa que a ideia de não oferecer umIntStream
(mas usarLongStream
para todos os tipos inteiros) tenha sido abandonada. Observe que, para o caso de uso sequencial, não há razão para usar stream:Collections.nCopies(8, 1).forEach(i -> System.out.println(i));
faz o mesmo,Collections.nCopies(8, 1).stream().forEach(i -> System.out.println(i));
mas pode ser ainda mais eficienteCollections.<Runnable>nCopies(8, () -> System.out.println(1)).forEach(Runnable::run);
LongStream.range
pior desempenho, porque tem dois mapas comLongFunction
dentro, enquantoncopies
tem três mapas comIntFunction
,ToLongFunction
eLongFunction
, portanto, todos os lambdas são monomórficos. Executar este teste no perfil de tipo pré-poluído (que é mais próximo do caso do mundo real) mostra quencopies
é 1,5x mais lento.for
loop antigo . Embora sua solução seja mais rápida do que oStream
código, meu palpite é que umfor
loop superaria qualquer um deles por uma margem significativa.Para ser completo, e também porque não pude evitar :)
Gerar uma sequência limitada de constantes é bastante próximo ao que você veria em Haskell, apenas com detalhamento de nível Java.
fonte
() -> 1
geraria apenas 1's, é isso intencional? Portanto, a saída seria1 1 1 1 1 1 1 1
.take 8 (repeat 1)
. Assylias praticamente cobriu todos os outros casos.Stream<T>
também possui umgenerate
método genérico para obter um fluxo infinito de algum outro tipo, que pode ser limitado da mesma maneira.Uma vez que uma função de repetição é definida em algum lugar como
Você pode usá-lo de vez em quando desta forma, por exemplo:
Para obter e equivalente ao de Haskell
Você poderia escrever
fonte
Runnable
paraFunction<Integer, ?>
e usandof.apply(i)
.Esta é minha solução para implementar a função de tempos. Sou um júnior, então admito que não seria o ideal, ficaria feliz em saber se isso não é uma boa ideia por qualquer motivo.
Aqui estão alguns exemplos de uso:
fonte