Dado um fluxo como { 0, 1, 2, 3, 4 }
,
como posso transformá-lo da forma mais elegante em determinada forma:
{ new Pair(0, 1), new Pair(1, 2), new Pair(2, 3), new Pair(3, 4) }
(presumindo, é claro, que eu tenha definido um par de classe)?
Edit: Isso não é estritamente sobre ints ou fluxos primitivos. A resposta deve ser geral para um fluxo de qualquer tipo.
java
java-8
java-stream
Aleksandr Dubinsky
fonte
fonte
list.stream().map(i -> new Pair(i, i+1));
Map.Entry
como uma classe Pair. (Certo, alguns podem considerar isso um hack, mas usar uma classe interna é útil.)Respostas:
Minha biblioteca StreamEx que estende fluxos padrão fornece um
pairMap
método para todos os tipos de fluxo. Para fluxos primitivos, não altera o tipo de fluxo, mas pode ser usado para fazer alguns cálculos. O uso mais comum é calcular diferenças:Para fluxo de objeto, você pode criar qualquer outro tipo de objeto. Minha biblioteca não fornece nenhuma nova estrutura de dados visível ao usuário
Pair
(essa é a parte do conceito de biblioteca). No entanto, se você tem sua própriaPair
aula e deseja usá-la, pode fazer o seguinte:Ou se você já tem algum
Stream
:Esta funcionalidade é implementada usando um divisor personalizado . Ele tem um overhead bastante baixo e pode paralelizar bem. É claro que funciona com qualquer fonte de fluxo, não apenas lista / array de acesso aleatório como muitas outras soluções. Em muitos testes, ele tem um desempenho muito bom. Aqui está um benchmark JMH onde encontramos todos os valores de entrada que precedem um valor maior usando diferentes abordagens (consulte esta questão).
fonte
StreamEx
implementaIterable
! Viva!)Stream
transformar um em umStreamEx
?StreamEx.of(stream)
. Existem outros métodos estáticos convenientes para criar o streamCollection
, arrayReader
, etc. Editou a resposta.pairMap
ordenado em fluxos sequenciais? Na verdade, eu gostaria de ter forPairsOrdered (), mas como não existe tal método, posso simular de alguma forma?stream.ordered().forPairs()
oustream().pairMap().forEachOrdered()
?pairMap
é a operação intermediária com função de mapeador sem estado que não interfere, a ordem não é especificada para ela da mesma forma que para simplesmap
. OforPairs
é não ordenado por especificação, mas as operações não ordenadas são ordenadas de fato para fluxos sequenciais. Seria bom se você formular seu problema original como uma questão stackoverflow separada para fornecer mais contexto.A biblioteca de streams Java 8 é principalmente voltada para a divisão de streams em pedaços menores para processamento paralelo, portanto, estágios de pipeline com estado são bastante limitados e tarefas como obter o índice do elemento stream atual e acessar elementos de stream adjacentes não são suportadas.
Uma maneira típica de resolver esses problemas, com algumas limitações, é claro, é conduzir o fluxo por índices e confiar em ter os valores sendo processados em alguma estrutura de dados de acesso aleatório, como um ArrayList do qual os elementos podem ser recuperados. Se os valores estivessem em
arrayList
, seria possível gerar os pares conforme solicitado fazendo algo assim:Claro que a limitação é que a entrada não pode ser um fluxo infinito. No entanto, esse pipeline pode ser executado em paralelo.
fonte
arrayList
) é na verdade uma coleção, por isso não a marquei como a resposta. (Mas parabéns pelo seu distintivo dourado!)Isso não é elegante, é uma solução hackeada, mas funciona para fluxos infinitos
Agora você pode limitar seu stream ao comprimento que desejar
PS Espero que haja uma solução melhor, algo como clojure
(partition 2 1 stream)
fonte
parallelStream
documento: "Para preservar o comportamento correto, esses parâmetros comportamentais não devem interferir e, na maioria dos casos, devem ser sem estado"Implementei um wrapper divisor que pega todos os
n
elementosT
do divisor original e produzList<T>
:O método a seguir pode ser usado para criar um fluxo consecutivo:
Uso de amostra:
fonte
List<E>
elementos. Cada lista contémn
elementos consecutivos do fluxo original. Verifique você mesmo;)(partition size step)
função e essa é a melhor maneira de obtê-la.ArrayDeque
para desempenho, em vez deLinkedList
.Você pode fazer isso com o método Stream.reduce () (não vi nenhuma outra resposta usando essa técnica).
fonte
Você pode fazer isso em cyclops-react (eu contribuo para esta biblioteca), usando o operador deslizante.
Ou
Supondo que o construtor de par pode aceitar uma coleção com 2 elementos.
Se você quiser agrupar por 4 e incrementar por 2, isso também é compatível.
Métodos estáticos equivalentes para criar uma visualização deslizante sobre java.util.stream.Stream também são fornecidos na classe Cyclops-streams StreamUtils .
Nota: - para operação de thread único, o ReactiveSeq seria mais apropriado. LazyFutureStream estende ReactiveSeq, mas é principalmente voltado para uso simultâneo / paralelo (é um Fluxo de Futuros).
LazyFutureStream estende ReactiveSeq, que estende Seq do incrível jOOλ (que estende java.util.stream.Stream), então as soluções apresentadas por Lukas também funcionariam com qualquer tipo de fluxo. Para qualquer pessoa interessada, as principais diferenças entre os operadores de janela / deslizante são a relação óbvia de troca de poder / complexidade e adequação para uso com fluxos infinitos (deslizar não consome o fluxo, mas armazena enquanto ele flui).
fonte
A biblioteca proton-pack fornece a funcionalidade em janela. Dada uma classe Pair e um Stream, você pode fazer assim:
Agora o
pairs
stream contém:fonte
st
duas vezes! Esta biblioteca pode resolver o problema usando um único fluxo?windowed
funcionalidade foi adicionada! Veja a edição.Encontrando pares sucessivos
Se você está disposto a usar uma biblioteca de terceiros e não precisa de paralelismo, então jOOλ oferece funções de janela no estilo SQL como segue
Produzindo
A
lead()
função acessa o próximo valor na ordem de passagem da janela.Encontrar sucessivos triplos / quádruplos / n-tuplas
Uma pergunta nos comentários estava pedindo uma solução mais geral, onde não pares, mas n-tuplas (ou possivelmente listas) deveriam ser coletados. Aqui está, portanto, uma abordagem alternativa:
Produzindo uma lista de listas
Sem o
filter(w -> w.count() == n)
, o resultado seriaIsenção de responsabilidade: eu trabalho para a empresa por trás do jOOλ
fonte
w.lead().lead()
?tuple(w.value(), w.lead(1), w.lead(2))
seria uma opção. Atualizei minha resposta com uma solução mais genérica paralength = n
.window()
não é uma operação preguiçosa que coleta todo o fluxo de entrada em alguma coleção intermediária e, em seguida, cria um novo fluxo a partir dele?Comparator
é usado para reordenar as janelas), uma otimização como essa seria possível e provavelmente será implementada no futuro.Streams.zip(..)
está disponível na Goiaba , para quem depende dela.Exemplo:
fonte
Podemos usar RxJava ( biblioteca de extensão reativa muito poderosa )
fonte
Observable.zip(obs, obs.skip(1), pair->{...})
até agora! Não sabia queObservable.buffer
tinha uma versão com um passo (e estou acostumado com ozip
truque do python). +1A operação é essencialmente com estado, então não é realmente o que os streams pretendem resolver - consulte a seção "Comportamentos sem estado" no javadoc :
Uma solução aqui é introduzir o estado em seu stream por meio de um contador externo, embora funcione apenas com um stream sequencial.
fonte
Stream
:! = "Lambdas".StreamEx
biblioteca também é um bom achado e pode ser uma resposta por si só. Meu comentário sobre "streams! = Lambdas" refere-se a você afirmando "A operação é essencialmente stateful, então não é realmente o que lambdas pretendem resolver." Acho que você quis dizer a palavra "streams".No seu caso, eu escreveria meu IntFunction personalizado que mantém o controle do último int passado e o usaria para mapear o IntStream original.
fonte
Para calcular diferenças sucessivas no tempo (x-valores) de uma série de tempo, eu utilizar o
stream
'scollect(...)
método:Onde o DifferenceCollector é algo assim:
Você provavelmente pode modificar isso para atender às suas necessidades.
fonte
Finalmente descobri uma maneira de enganar o Stream.reduce para poder lidar ordenadamente com pares de valores; há uma infinidade de casos de uso que requerem esse recurso que não aparece naturalmente no JDK 8:
O truque que uso é o direito de retorno; declaração.
fonte
reduce
dá garantias suficientes para que isso funcione.Uma solução elegante seria usar zip . Algo como:
Isso é muito conciso e elegante, no entanto, usa uma lista como entrada. Uma fonte de fluxo infinito não pode ser processada dessa maneira.
Outro problema (muito mais problemático) é que o zip junto com a classe Streams inteira foi recentemente removido da API. O código acima funciona apenas com b95 ou versões anteriores. Portanto, com o JDK mais recente, eu diria que não há uma solução elegante no estilo FP e, no momento, podemos apenas esperar que, de alguma forma, o zip seja reintroduzido na API.
fonte
zip
foi removido. Não me lembro de tudo o que havia naStreams
classe, mas algumas coisas migraram para métodos estáticos naStream
interface e também existem classesStreamSupport
eStream.Builder
.zip
? Qualquer razão pedante que possa ser inventada não justifica matarzip
.Este é um problema interessante. Minha tentativa híbrida é inferior a alguma boa?
Eu acredito que não se presta ao processamento paralelo e, portanto, pode ser desqualificado.
fonte
Stream
, não aList
. Claro, podemos extrair um iterador de um Stream também, então essa pode ser uma solução válida. No entanto, é uma abordagem original.Como outros observaram, devido à natureza do problema, existe alguma condição necessária.
Eu me deparei com um problema semelhante, no qual eu queria o que era essencialmente a função LEAD do Oracle SQL. Minha tentativa de implementar isso está abaixo.
fonte
Você pode conseguir isso usando uma fila limitada para armazenar os elementos que fluem pelo fluxo (que se baseia na ideia que descrevi em detalhes aqui: É possível obter o próximo elemento no fluxo? )
O exemplo abaixo define primeiro a instância da classe BoundedQueue que armazenará elementos que passam pelo fluxo (se você não gosta da ideia de estender LinkedList, consulte o link mencionado acima para uma abordagem alternativa e mais genérica). Mais tarde, você apenas combina dois elementos subsequentes em uma instância de Pair:
fonte
Eu concordo com @aepurniet, mas em vez disso, você deve usar mapToObj
fonte
Execute um
for
loop que vai de 0 alength-1
de seu streamfonte