Eu tenho um conjunto de dados representado por um fluxo Java 8:
Stream<T> stream = ...;
Eu posso ver como filtrá-lo para obter um subconjunto aleatório - por exemplo
Random r = new Random();
PrimitiveIterator.OfInt coin = r.ints(0, 2).iterator();
Stream<T> heads = stream.filter((x) -> (coin.nextInt() == 0));
Também posso ver como reduzir esse fluxo para obter, por exemplo, duas listas representando duas metades aleatórias do conjunto de dados e depois transformá-las novamente em fluxos. Mas, existe uma maneira direta de gerar dois fluxos a partir do inicial? Algo como
(heads, tails) = stream.[some kind of split based on filter]
Obrigado por qualquer insight.
java
java-8
java-stream
user1148758
fonte
fonte
Stream
em múltiplosStream
s sem conversão intermediária , embora eu ache que as pessoas que chegaram a essa pergunta estão realmente procurando o caminho para alcançá-lo, independentemente de tal restrição, que é a resposta de Mark. Isso pode ser devido ao fato de a pergunta no título não ser a mesma da descrição .Respostas:
Não exatamente. Você não pode obter dois
Stream
s de um; isso não faz sentido - como você iteraria uma sem precisar gerar a outra ao mesmo tempo? Um fluxo pode ser operado apenas uma vez.No entanto, se você deseja despejá-los em uma lista ou algo assim, você pode fazer
fonte
stream.collect(...)
thread-safe predefinidoCollectors
, que funciona bem mesmo em coleções não thread-safe (sem contenção de bloqueio sincronizado). Melhor resposta por @MarkJeronimus.Um coletor pode ser usado para isso.
Collectors.partitioningBy()
factory.Isso criará um
Map
deBoolean
paraList
e colocará itens em uma ou outra lista com base em aPredicate
.Nota: Como o fluxo precisa ser consumido inteiro, isso não pode funcionar em fluxos infinitos. E como o fluxo é consumido de qualquer maneira, esse método simplesmente os coloca em Listas, em vez de criar um novo fluxo com memória. Você sempre pode transmitir essas listas se precisar de fluxos como saída.
Além disso, não há necessidade do iterador, nem mesmo no exemplo somente de cabeçote que você forneceu.
Collectors.groupingBy()
fábrica.Caso os fluxos não sejam
Stream
, mas um dos fluxos primitivosIntStream
, esse.collect(Collectors)
método não estará disponível. Você terá que fazer isso da maneira manual sem uma fábrica de colecionadores. Sua implementação é assim:[Exemplo 2.0 desde 16/04 2020]
Neste exemplo, inicializo o ArrayLists com o tamanho total da coleção inicial (se é que isso é conhecido). Isso evita eventos de redimensionamento, mesmo no pior cenário, mas pode potencialmente devorar 2 * N * T de espaço (N = número inicial de elementos, T = número de encadeamentos). Para trocar o espaço pela velocidade, você pode deixá-lo de fora ou usar seu palpite melhor, como o maior número esperado de elementos em uma partição (normalmente um pouco acima de N / 2 para uma divisão equilibrada).
Espero não ofender ninguém usando o método Java 9. Para a versão do Java 8, consulte o histórico de edições.
fonte
stream.boxed().collect(...);
! Ele fará o que foi anunciado: converta o primitivoIntStream
para aStream<Integer>
versão em caixa .(map, x) -> { boolean partition = p.test(x); List<Integer> list = map.get(partition); list.add(x); }
você pode simplesmente usar(map, x) -> map.get(p.test(x)).add(x)
. Além disso, não vejo nenhum motivo para acollect
operação não ser segura para threads. Funciona exatamente como deveria funcionar e muito próximo de comoCollectors.partitioningBy(p)
funcionaria. Mas eu usaria um emIntPredicate
vez dePredicate<Integer>
quando não estiver usandoboxed()
, para evitar o boxe duas vezes.Eu me deparei com essa pergunta e sinto que um fluxo bifurcado tem alguns casos de uso que podem ser válidos. Escrevi o código abaixo como consumidor para que ele não faça nada, mas você pode aplicá-lo a funções e qualquer outra coisa que possa encontrar.
Agora sua implementação de código pode ser algo como isto:
fonte
Infelizmente, o que você pede é desaprovado diretamente no JavaDoc of Stream :
Você pode solucionar esse problema usando
peek
outros métodos, se realmente desejar esse tipo de comportamento. Nesse caso, o que você deve fazer é, em vez de tentar fazer backup de dois fluxos da mesma fonte original do fluxo com um filtro de bifurcação, você duplicaria o fluxo e filtraria cada uma das duplicatas adequadamente.No entanto, convém reconsiderar se a
Stream
é a estrutura apropriada para seu caso de uso.fonte
List<Stream> forkStream(Stream s)
mas meus fluxos resultantes serão pelo menos parcialmente apoiados por coleções e não diretamente pelo fluxo subjacente, em vez de dizerfilter
que não é uma operação de fluxo terminal.Isso é contra o mecanismo geral do Stream. Digamos que você possa dividir o Stream S0 em Sa e Sb como desejar. Executar qualquer operação do terminal, digamos
count()
, no Sa, necessariamente "consumirá" todos os elementos em S0. Portanto, o Sb perdeu sua fonte de dados.Anteriormente,
tee()
eu acho que o Stream tinha um método que duplicava um stream para dois. Foi removido agora.O Stream possui um método peek (); talvez você possa usá-lo para atender aos seus requisitos.
fonte
peek
é exatamente o que costumava sertee
.não exatamente, mas você pode conseguir o que precisa invocando
Collectors.groupingBy()
. você cria uma nova coleção e pode instanciar fluxos nessa nova coleção.fonte
Essa foi a resposta menos ruim que eu consegui encontrar.
Isso pega um fluxo de números inteiros e os divide em 5. Para aqueles com mais de 5, ele filtra apenas números pares e os coloca em uma lista. De resto, junta-os a |.
saídas:
Não é o ideal, pois reúne tudo em coleções intermediárias que quebram o fluxo (e tem muitos argumentos!)
fonte
Eu me deparei com essa pergunta enquanto procurava uma maneira de filtrar certos elementos de um fluxo e registrá-los como erros. Portanto, eu realmente não precisava dividir o fluxo, mas anexar uma ação de encerramento prematura a um predicado com sintaxe discreta. Isto é o que eu vim com:
fonte
Versão mais curta que usa Lombok
fonte
E se:
fonte