Posso adicionar fluxos ou elementos extras, como este:
Stream stream = Stream.concat(stream1, Stream.concat(stream2, Stream.of(element));
E posso adicionar coisas novas à medida que vou, assim:
Stream stream = Stream.concat(
Stream.concat(
stream1.filter(x -> x!=0), stream2)
.filter(x -> x!=1),
Stream.of(element))
.filter(x -> x!=2);
Mas isso é feio, porque concat
é estático. Se concat
fosse um método de instância, os exemplos acima seriam muito mais fáceis de ler:
Stream stream = stream1.concat(stream2).concat(element);
E
Stream stream = stream1
.filter(x -> x!=0)
.concat(stream2)
.filter(x -> x!=1)
.concat(element)
.filter(x -> x!=2);
Minha pergunta é:
1) Existe alguma boa razão para concat
ser estático? Ou existe algum método de instância equivalente que estou faltando?
2) De qualquer forma, existe uma maneira melhor de fazer isso?
java
concat
java-8
java-stream
MarcG
fonte
fonte
Respostas:
Se você adicionar importações estáticas para Stream.concat e Stream.of , o primeiro exemplo poderá ser escrito da seguinte maneira:
A importação de métodos estáticos com nomes genéricos pode resultar em um código que fica difícil de ler e manter ( poluição do namespace ). Portanto, pode ser melhor criar seus próprios métodos estáticos com nomes mais significativos. No entanto, para demonstração, continuarei com esse nome.
Com esses dois métodos estáticos (opcionalmente em combinação com importações estáticas), os dois exemplos podem ser escritos da seguinte maneira:
O código agora é significativamente mais curto. No entanto, concordo que a legibilidade não melhorou. Então, eu tenho outra solução.
Em muitas situações, os coletores podem ser usados para estender a funcionalidade dos fluxos. Com os dois coletores na parte inferior, os dois exemplos podem ser escritos da seguinte maneira:
A única diferença entre a sintaxe desejada e a sintaxe acima é que você deve substituir concat (...) por collect (concat (...)) . Os dois métodos estáticos podem ser implementados da seguinte forma (opcionalmente usado em combinação com importações estáticas):
Obviamente, há uma desvantagem nesta solução que deve ser mencionada. collect é uma operação final que consome todos os elementos do fluxo. Além disso, o coletor concat cria um ArrayList intermediário cada vez que é usado na cadeia. Ambas as operações podem ter um impacto significativo no comportamento do seu programa. No entanto, se a legibilidade for mais importante que o desempenho , ainda poderá ser uma abordagem muito útil.
fonte
concat
colecionador muito legível. Parece estranho um método estático de parâmetro único chamado assim, e também usarcollect
para concatenação.It's a bad idea to import static methods with names
? Estou realmente interessado - acho que torna o código mais conciso e legível, e muitas pessoas que eu pedi pensaram o mesmo. Gostaria de fornecer alguns exemplos de por que isso geralmente é ruim?compare(reverse(getType(42)), of(6 * 9).hashCode())
? Observe que eu não disse que importações estáticas são uma má idéia, mas importações estáticas para nomes genéricos comoof
econcat
são.Infelizmente, essa resposta provavelmente é de pouca ou nenhuma ajuda, mas fiz uma análise forense da lista de mala direta do Java Lambda para ver se encontrava a causa desse design. Foi isso que eu descobri.
No começo, havia um método de instância para Stream.concat (Stream)
Na lista de discussão, posso ver claramente que o método foi originalmente implementado como um método de instância, como você pode ler neste tópico de Paul Sandoz, sobre a operação concat.
Nele, discutem-se os problemas que poderiam surgir daqueles casos em que o fluxo poderia ser infinito e o que concatenação significaria nesses casos, mas não creio que esse tenha sido o motivo da modificação.
Você vê neste outro encadeamento que alguns usuários iniciais do JDK 8 questionaram sobre o comportamento do método da instância concat quando usado com argumentos nulos.
Esse outro segmento revela, no entanto, que o design do método concat estava em discussão.
Refatorado para Streams.concat (Stream, Stream)
Mas sem nenhuma explicação, de repente, os métodos foram alterados para métodos estáticos, como você pode ver neste tópico sobre a combinação de fluxos . Esse talvez seja o único segmento de correio que esclarece um pouco essa alteração, mas não estava claro o suficiente para eu determinar o motivo da refatoração. Mas podemos ver que eles fizeram um commit no qual sugeriram mover o
concat
método para fora daStream
classe auxiliarStreams
.Refatorado para Stream.concat (Stream, Stream)
Mais tarde, foi movido novamente de
Streams
paraStream
, mas novamente, nenhuma explicação para isso.Portanto, o motivo do design não está totalmente claro para mim e não consegui encontrar uma boa explicação. Eu acho que você ainda pode fazer a pergunta na lista de discussão.
Algumas alternativas para a concatenação de fluxo
Este outro tópico de Michael Hixson discute / pergunta sobre outras maneiras de combinar / concat streams
fonte
public static <T> Stream<T> concat(Stream<T>... streams) { return Stream.of(streams).reduce(Stream.empty(), Stream::concat);}
@SafeVarargs private static <T> Stream<T> concat(Stream<? extends T>... streams) { return Stream.of(streams).reduce(Stream.empty(),Stream::concat).map(Function.identity());}
Function.identity()
mapa? Afinal, ele retorna o mesmo argumento que recebe. Isso não deve ter efeito no fluxo resultante. Estou esquecendo de algo?return Stream.of(streams).reduce(Stream.empty(),Stream::concat)
retorna o fluxo <? estende T>. (Someting <T> é o subtipo de Algo <? estende T>, não o contrário, portanto não pode ser convertido).map(identity())
Elenco adicional <? estende T> para <T>. Isso acontece graças à combinação de 'tipos de destino' java 8 de argumentos de método e tipos de retorno e assinatura do método map (). Na verdade, é Function. <T> identity ().? extends T
, já que você pode usar a conversão de captura . De qualquer forma, aqui está o meu trecho de código da lista principal Vamos continuar a discussão na lista principal.Minha biblioteca StreamEx estende a funcionalidade da API Stream. Em particular, oferece métodos como anexar e anexar que resolvem esse problema (eles usam internamente
concat
). Esses métodos podem aceitar outro fluxo ou coleção ou matriz de varargs. Usando minha biblioteca, seu problema pode ser resolvido desta maneira (observe quex != 0
parece estranho para um fluxo não primitivo):A propósito, também há um atalho para sua
filter
operação:fonte
Apenas faça:
onde
identity()
é uma importação estática deFunction.identity()
.Concatenar vários fluxos em um fluxo é o mesmo que achatar um fluxo.
No entanto, infelizmente, por algum motivo, não existe um
flatten()
métodoStream
, então você deve usarflatMap()
com a função de identidade.fonte
Você pode usar o método Guava , que será muito curto com importações estáticas:
Streams
.
concat(Stream<? extends T>... streams)
fonte
Se você não se importa em usar as bibliotecas de terceiros, o cyclops-react possui um tipo de fluxo estendido que permitirá que você faça exatamente isso através dos operadores de acréscimo / pré-acréscimo.
Os valores individuais, matrizes, iterables, Streams ou reactive-streams Publishers podem ser anexados e anexados como métodos de instância.
[Divulgação Sou o principal desenvolvedor do cyclops-react]
fonte
No final do dia, não estou interessado em combinar fluxos, mas em obter o resultado combinado do processamento de cada elemento de todos esses fluxos.
Embora a combinação de fluxos possa ser complicada (portanto, este segmento), combinar seus resultados de processamento é bastante fácil.
A chave a resolver é criar seu próprio coletor e garantir que a função do fornecedor para o novo coletor retorne a mesma coleção sempre ( não uma nova ), o código abaixo ilustra essa abordagem.
fonte
Que tal escrever seu próprio método concat?
Isso pelo menos torna seu primeiro exemplo muito mais legível.
fonte