Pergunta geral: Qual é a maneira correta de reverter um fluxo? Supondo que não sabemos em que tipo de elementos esse fluxo consiste, qual é a maneira genérica de reverter qualquer fluxo?
Pergunta específica:
IntStream
fornece o método range para gerar números inteiros em um intervalo específico IntStream.range(-range, 0)
, agora que eu quero revertê-lo, mudar o intervalo de 0 para negativo não funcionará, também não posso usarInteger::compare
List<Integer> list = Arrays.asList(1,2,3,4);
list.stream().sorted(Integer::compare).forEach(System.out::println);
com IntStream
eu vou receber esse erro do compilador
Erro: (191, 0) ajc: o método
sorted()
no tipoIntStream
não é aplicável aos argumentos (Integer::compare
)
O que estou perdendo aqui?
IntStream
não tem.sorted(Comparator)
método; você tem que passar por umStream<Integer>
primeiro e reverter lá antes de produzir umIntStream
IntStream.range(0, n)
ordem inversa, faça algo parecidomap(i -> n - i - 1)
. Não há necessidade de fazer boxe e classificação.1, 3, 2
, qual é o resultado esperado? Deseja como o fluxo invertido2, 3, 1
ou como o fluxo classificado3, 2, 1
?Respostas:
Para a questão específica de gerar um reverso
IntStream
, tente algo como isto:Isso evita boxe e classificação.
Para a questão geral de como reverter um fluxo de qualquer tipo, não sei se existe uma maneira "adequada". Existem algumas maneiras em que posso pensar. Ambos acabam armazenando os elementos do fluxo. Não sei como reverter um fluxo sem armazenar os elementos.
Essa primeira maneira armazena os elementos em uma matriz e os lê em um fluxo na ordem inversa. Observe que, como não sabemos o tipo de tempo de execução dos elementos do fluxo, não podemos digitar a matriz corretamente, exigindo uma conversão não verificada.
Outra técnica usa coletores para acumular os itens em uma lista invertida. Isso faz muitas inserções na frente dos
ArrayList
objetos, então há muitas cópias acontecendo.Provavelmente, é possível escrever um coletor de reversão muito mais eficiente usando algum tipo de estrutura de dados personalizada.
UPDATE 2016-01-29
Como essa pergunta recebeu um pouco de atenção recentemente, acho que devo atualizar minha resposta para resolver o problema com a inserção na frente
ArrayList
. Isso será terrivelmente ineficiente com um grande número de elementos, exigindo cópia de O (N ^ 2).É preferível usar um
ArrayDeque
, que suporta com eficiência a inserção na frente. Uma pequena ruga é que não podemos usar a forma de três argumentos deStream.collect()
; requer que o conteúdo do segundo argumento seja mesclado com o primeiro argumento e não há operação em massa "adicionar tudo à frente"Deque
. Em vez disso, usamosaddAll()
para anexar o conteúdo do primeiro argumento ao final do segundo e depois retornamos o segundo. Isso requer o uso doCollector.of()
método de fábrica.O código completo é este:
O resultado é um em
Deque
vez de umList
, mas isso não deve ser um grande problema, pois pode ser facilmente iterado ou transmitido na ordem agora invertida.fonte
IntStream.iterate(to-1, i->i-1).limit(to-from)
.limit(endExcl-(long)startIncl)
, mas para fluxos tão grandes, é muito desencorajado de qualquer maneira, pois é muito menos eficiente que arange
solução baseada. No momento em que escrevi o comentário, não estava ciente da diferença de eficiência.Solução elegante
fonte
Comparable
...Muitas das soluções aqui classificam ou revertem a
IntStream
, mas isso requer desnecessariamente armazenamento intermediário. A solução de Stuart Marks é o caminho a seguir:Também lida corretamente com o estouro, passando neste teste:
fonte
Estreams
nome (vou removê-lo da postagem). É uma das classes de utilidade interna da nossa empresa, que usamos para suplementarjava.util.stream.Stream
osstatic
métodos.StreamEx
especificando a etapa:IntStreamEx.rangeClosed(from-1, to, -1)
Pergunta Geral:
O fluxo não armazena nenhum elemento.
Portanto, iterar elementos na ordem inversa não é possível sem armazenar os elementos em alguma coleção intermediária.
Atualização: Alterada LinkedList para ArrayDeque (melhor), veja aqui para detalhes
Impressões:
A propósito, o uso do
sort
método não está correto como ele classifica, NÃO reverte (assumindo que o fluxo possa ter elementos não ordenados)Pergunta específica:
Achei isso simples, mais fácil e intuitivo ( comentário copiado do @Holger )
fonte
sorted
edistinct
na verdade armazenam um resultado intermediário. Consulte os documentos da API do pacote para obter mais informações sobre isso.No storage
na mesma página. Mesmo ele armazena nós não pode ter acesso a esse armazenamento (assimNo storage
é bom eu acho)sem lib externo ...
fonte
Se implementada
Comparable<T>
(ex.Integer
,String
,Date
), Você pode fazê-lo usandoComparator.reverseOrder()
.fonte
Stream.of(1,3,2)
o resultado seriaStream.of(3,2,1)
NÃOStream.of(2,3,1)
Você pode definir seu próprio coletor que coleta os elementos na ordem inversa:
E use-o como:
Eu uso um ArrayList para encaminhar para inserir com eficiência os itens coletados (no final da lista) e Guava Lists.reverse para fornecer com eficiência uma visão invertida da lista sem fazer outra cópia.
Aqui estão alguns casos de teste para o coletor personalizado:
fonte
O Cyclops-react StreamUtils possui um método de fluxo reverso ( javadoc ).
Ele funciona coletando para um ArrayList e, em seguida, fazendo uso da classe ListIterator que pode iterar em qualquer direção, para iterar para trás na lista.
Se você já possui uma Lista, será mais eficiente
fonte
Eu sugeriria o uso do jOOλ , é uma ótima biblioteca que adiciona muitas funcionalidades úteis aos fluxos e lambdas do Java 8.
Você pode fazer o seguinte:
Simples assim. É uma biblioteca bastante leve e vale a pena adicionar a qualquer projeto Java 8.
fonte
Aqui está a solução que eu criei:
depois, usando esses comparadores:
fonte
Collections.reverseOrder()
existe desde Java 1.2 e funciona comInteger
...Que tal esse método utilitário?
Parece funcionar com todos os casos sem duplicação.
fonte
fonte
Maneira mais simples (coleta simples - suporta fluxos paralelos):
Maneira avançada (suporta fluxos paralelos de maneira contínua):
Observe que você pode estender rapidamente para outro tipo de fluxos (IntStream, ...).
Teste:
Resultados:
Notas adicionais: Não
simplest way
é tão útil quando usado com outras operações de fluxo (a junção de coleta quebra o paralelismo). Eleadvance way
não tem esse problema e mantém também as características iniciais do fluxo, por exemploSORTED
, e, portanto, é o caminho a ser usado com outras operações de fluxo após o inverso.fonte
Pode-se escrever um coletor que coleta elementos em ordem inversa:
E use-o assim:
Resposta original (contém um erro - ele não funciona corretamente para fluxos paralelos):
Um método reverso de fluxo de uso geral pode se parecer com:
fonte
Não é puramente Java8, mas se você usar o método Lists.reverse () da guava em conjunto, poderá conseguir isso facilmente:
fonte
No que diz respeito à questão específica de gerar um reverso
IntStream
:a partir do Java 9, você pode usar a versão de três argumentos do
IntStream.iterate(...)
:Onde:
IntStream.iterate(int seed, IntPredicate hasNext, IntUnaryOperator next);
seed
- o elemento inicial;hasNext
- um predicado a ser aplicado aos elementos para determinar quando o fluxo deve terminar;next
- uma função a ser aplicada ao elemento anterior para produzir um novo elemento.fonte
Para referência, eu estava olhando para o mesmo problema, queria juntar o valor da string dos elementos do fluxo na ordem inversa.
itemList = {último, meio, primeiro} => primeiro, meio, último
Comecei a usar uma coleção intermediária com
collectingAndThen
de comonad ou oArrayDeque
colecionador de Stuart Marks , embora não estivesse feliz com a coleção intermediária e transmitindo novamenteEntão, eu repeti a resposta de Stuart Marks que estava usando a
Collector.of
fábrica, que tem o interessante finalizador lambda.Como nesse caso o fluxo não é paralelo, o combinador não é relevante, estou usando
insert
assim mesmo para manter a consistência do código, mas isso não importa, pois dependeria de qual construtor de string é construído primeiro.Eu olhei para o StringJoiner, no entanto, ele não tem um
insert
método.fonte
Respondendo a perguntas específicas de reversão com o IntStream, abaixo funcionou para mim:
fonte
ArrayDeque
são mais rápidos na pilha do que uma Stack ou LinkedList. "push ()" insere elementos na frente do Dequefonte
Inverter sequência ou qualquer matriz
A divisão pode ser modificada com base no delimitador ou no espaço
fonte
a solução mais simples é usar
List::listIterator
eStream::generate
fonte
Stream.generate()
gera em fluxo infinito, então a chamada paralimit()
é muito importante aqui.É assim que eu faço.
Não gosto da ideia de criar uma nova coleção e inverter a iteração.
A ideia do mapa IntStream # é bastante interessante, mas eu prefiro o método iterado IntStream #, pois acho que a idéia de uma contagem regressiva para Zero melhor expressa com o método iterado e mais fácil de entender em termos de caminhar a matriz de trás para a frente.
Aqui estão alguns testes para provar que funciona:
fonte
Em tudo isso, não vejo a resposta que gostaria de ir primeiro.
Esta não é exatamente uma resposta direta à pergunta, mas é uma solução potencial para o problema.
Basta criar a lista ao contrário em primeiro lugar. Se possível, use um LinkedList em vez de um ArrayList e, quando adicionar itens, use "Push" em vez de adicionar. A lista será criada na ordem inversa e será transmitida corretamente, sem qualquer manipulação.
Isso não se aplica aos casos em que você está lidando com matrizes ou listas primitivas que já são usadas de várias maneiras, mas funciona bem em um número surpreendente de casos.
fonte
Este método funciona com qualquer fluxo e é compatível com Java 8:
fonte
A maneira mais genérica e mais fácil de reverter uma lista será:
fonte
Comparator
. Como resultado, ninguém pode garantir que esse "truque" funcione em qualquer versão futura do Java com qualquer algoritmo de classificação. O mesmo truque não funciona para fluxo paralelo, por exemplo, como o algoritmo de classificação paralela usa deComparator
maneira diferente. Para classificação seqüencial, funciona puramente por acaso. Eu não recomendaria ninguém para usar esta solução.System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
public static <T> void reverseHelper(List<T> li){ li.parallelStream() .sorted((x,y)->-1) .collect(Collectors.toList()) .forEach(System.out::println); }
reverseHelper(IntStream.range(0, 8193).boxed().collect(Collectors.toList()))
(o resultado pode depender do número de núcleos).Java 8 maneira de fazer isso:
fonte