Existe uma operação de fluxo do Java 8 que limita um (potencialmente infinito) Stream
até que o primeiro elemento falhe ao corresponder a um predicado?
No Java 9, podemos usar takeWhile
como no exemplo abaixo para imprimir todos os números menores que 10.
IntStream
.iterate(1, n -> n + 1)
.takeWhile(n -> n < 10)
.forEach(System.out::println);
Como não existe essa operação no Java 8, qual é a melhor maneira de implementá-la de maneira geral?
java
java-8
java-stream
MForster
fonte
fonte
IntStream.iterate(1, n->n<10, n->n+1).forEach(System.out::print);
Respostas:
Essa operação deve ser possível com um Java 8
Stream
, mas não pode necessariamente ser feita com eficiência - por exemplo, você não pode necessariamente paralelizar uma operação, pois é necessário examinar os elementos em ordem.A API não fornece uma maneira fácil de fazê-lo, mas provavelmente a maneira mais simples é seguir
Stream.iterator()
, agruparIterator
para ter uma implementação "demorada" e, em seguida, voltar para aeSpliterator
depois aStream
. Ou - talvez - encapsule oSpliterator
, embora ele realmente não possa mais ser dividido nesta implementação.Aqui está uma implementação não testada de
takeWhile
em umSpliterator
:fonte
Operações
takeWhile
edropWhile
foram adicionados ao JDK 9. Seu código de exemplose comportará exatamente como você espera quando compilado e executado no JDK 9.
O JDK 9 foi lançado. Está disponível para download aqui: http://jdk.java.net/9/
fonte
takeWhile
/dropWhile
: download.java.net/jdk9/docs/api/java/util/stream/Stream.htmltakeWhile
edropWhile
, em vez delimitWhile
eskipWhile
, por coerência com a API existente?takeWhile
edropWhile
são bastante difundidos, ocorrendo em Scala, Python, Groovy, Ruby, Haskell e Clojure. A assimetria comskip
elimit
é lamentável. Talvezskip
elimit
deveria ter sido chamadodrop
etake
, mas aqueles não são tão intuitiva a menos que você já está familiarizado com Haskell.dropXXX
etakeXXX
são os termos mais populares, mas eu posso pessoalmente ao vivo com a mais SQL-esquelimitXXX
eskipXXX
. I encontrar este novo assimetria muito mais confuso do que a escolha individual de termos ... :) (btw: Scala também temdrop(int)
etake(int)
)allMatch()
é uma função de curto-circuito, para que você possa usá-la para interromper o processamento. A principal desvantagem é que você deve fazer o teste duas vezes: uma vez para ver se deve processá-lo e outra vez para continuar.fonte
Stream.allMatch()
é uma operação em curto-circuito . Portanto, isso será concluído mesmo em um fluxo infinito comoIntStream.iterate()
. Obviamente, em retrospecto, essa é uma otimização sensata.peek
. Se o encontrasse no próximo mês, levaria um minuto para me perguntar por que o programador antes de mim verificou seallMatch
e depois ignorou a resposta.Como acompanhamento da resposta do @StuartMarks . Minha biblioteca StreamEx possui a
takeWhile
operação que é compatível com a implementação atual do JDK-9. Quando executado no JDK-9, ele apenas delega para a implementação do JDK (através daMethodHandle.invokeExact
qual é realmente rápido). Ao executar no JDK-8, a implementação "polyfill" será usada. Portanto, usando minha biblioteca, o problema pode ser resolvido assim:fonte
takeWhile
é uma das funções fornecidas pela biblioteca protonpack .fonte
Atualização: o Java 9
Stream
agora vem com um takeWhile método .Não há necessidade de hacks ou outras soluções. Apenas use isso!
Estou certo de que isso pode ser bastante aprimorado: (talvez alguém possa torná-lo seguro para threads)
Um hack com certeza ... Não é elegante - mas funciona ~: D
fonte
Você pode usar o java8 + rxjava .
fonte
Na verdade, existem 2 maneiras de fazer isso no Java 8 sem bibliotecas extras ou usando o Java 9.
Se você quiser imprimir números de 2 a 20 no console, faça o seguinte:
ou
A saída é nos dois casos:
Ninguém mencionou anyMatch ainda. Esta é a razão para este post.
fonte
Essa é a fonte copiada do JDK 9 java.util.stream.Stream.takeWhile (Predicate). Uma pequena diferença para trabalhar com o JDK 8.
fonte
Aqui está uma versão feita em ints - conforme solicitado na pergunta.
Uso:
Aqui está o código para o StreamUtil:
fonte
Vá para a biblioteca AbacusUtil . Ele fornece a API exata que você deseja e muito mais:
Declaração: Sou o desenvolvedor do AbacusUtil.
fonte
Você não pode abortar um fluxo, exceto por uma operação de terminal em curto-circuito, o que deixaria alguns valores de fluxo não processados, independentemente de seu valor. Mas se você quiser apenas evitar operações em um fluxo, poderá adicionar uma transformação e um filtro ao fluxo:
Isso transforma o fluxo de coisas em nulos quando as coisas atendem a alguma condição e depois filtra nulos. Se você estiver disposto a entrar em efeitos colaterais, poderá definir o valor da condição como verdadeiro assim que alguma coisa for encontrada, para que todas as coisas subsequentes sejam filtradas independentemente do seu valor. Mas, se não, você pode economizar muito (se não todo) o processamento, filtrando valores do fluxo que não deseja processar.
fonte
Até eu estava tendo um requisito semelhante - invoque o serviço da Web, se falhar, tente novamente três vezes. Se falhar, mesmo após essas várias tentativas, envie uma notificação por email. Depois de pesquisar muito,
anyMatch()
veio como um salvador. Meu código de exemplo da seguinte maneira. No exemplo a seguir, se o método webServiceCall retornar true na primeira iteração, o fluxo não iterará mais como chamamosanyMatch()
. Eu acredito que é isso que você está procurando.fonte
Se você souber a quantidade exata de repetições que serão executadas, poderá fazer
fonte
em vez de pico, você pode usar o mapToObj para retornar o objeto ou mensagem final
fonte
Se você tiver um problema diferente, uma solução diferente pode ser necessária, mas para o seu problema atual, eu simplesmente diria:
fonte
Pode ser um pouco estranho, mas é para isso que temos,
List<T>
e nãoStream<T>
.Primeiro você precisa ter um
take
método util. Este método leva os primeirosn
elementos:simplesmente funciona como
scala.List.take
agora será bastante simples escrever um
takeWhile
método baseado emtake
funciona assim:
essa implementação itera a lista parcialmente por algumas vezes, mas não adiciona
O(n^2)
operações de adição . Espero que seja aceitável.fonte
Eu tenho outra solução rápida implementando isso (que é realmente imundo, mas você entendeu):
fonte
current
nunca.equals(e)
, você terá um loop infinito. Ambos, mesmo se você aplicar posteriormente, por exemplo.limit(1)
. Isso é muito pior do que "impuro" .Aqui está minha tentativa de usar apenas a biblioteca Java Stream.
fonte
filter
predicado deve ser apátrida.System.out.println
é um efeito colateral.