Parece que estou tendo problemas para entender como o Java compõe operações de fluxo em um pipeline de fluxo.
Ao executar o seguinte código
public
static void main(String[] args) {
StringBuilder sb = new StringBuilder();
var count = Stream.of(new String[]{"1", "2", "3", "4"})
.map(sb::append)
.count();
System.out.println(count);
System.out.println(sb.toString());
}
O console apenas imprime 4
. O StringBuilder
objeto ainda tem o valor ""
.
Quando adiciono a operação de filtro: filter(s -> true)
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
var count = Stream.of(new String[]{"1", "2", "3", "4"})
.filter(s -> true)
.map(sb::append)
.count();
System.out.println(count);
System.out.println(sb.toString());
}
A saída muda para:
4
1234
Como essa operação de filtro aparentemente redundante altera o comportamento do pipeline de fluxo composto?
java
java-stream
atalantus
fonte
fonte
Respostas:
A
count()
operação do terminal, na minha versão do JDK, acaba executando o seguinte código:Se houver uma
filter()
operação no pipeline de operações, o tamanho do fluxo, que é conhecido inicialmente, não poderá mais ser conhecido (poisfilter
poderá rejeitar alguns elementos do fluxo). Portanto, oif
bloco não é executado, as operações intermediárias são executadas e o StringBuilder é modificado.Por outro lado, se você tiver apenas
map()
no pipeline, é garantido que o número de elementos no fluxo seja o mesmo que o número inicial de elementos. Portanto, o bloco if é executado e o tamanho é retornado diretamente sem avaliar as operações intermediárias.Observe que o lambda passado para
map()
viola o contrato definido na documentação: deveria ser uma operação sem estado e sem interferências, mas não é sem estado. Portanto, ter um resultado diferente nos dois casos não pode ser considerado um bug.fonte
flatMap()
poderia alterar o número de elementos, foi por isso que estava inicialmente ansioso (agora preguiçoso)? Portanto, a alternativa seria usarforEach()
e contar separadamente se,map()
na sua forma atual, violar o contrato, eu acho.4 1234
sem utilizar o filtro extra ou produzir efeitos colaterais na operação map ()?int count = array.length; String result = String.join("", array);
Collectors.joining("")
No jdk-9 , foi claramente documentado nos documentos java
Nota da API:
fonte
Não é para isso que serve o .map. É suposto ser usado para transformar um fluxo de "Something" em um fluxo de "Something Else". Nesse caso, você está usando o mapa para anexar uma string a um Stringbuilder externo, após o qual você tem um fluxo de "Stringbuilder", cada um dos quais foi criado pela operação de mapa anexando um número ao Stringbuilder original.
Seu fluxo não faz nada com os resultados mapeados no fluxo, portanto, é perfeitamente razoável supor que a etapa possa ser ignorada pelo processador de fluxo. Você está contando com efeitos colaterais para fazer o trabalho, o que quebra o modelo funcional do mapa. Você seria melhor atendido usando o forEach para fazer isso. Faça a contagem inteiramente como um fluxo separado ou coloque um contador usando AtomicInt no forEach.
O filtro obriga a executar o conteúdo do fluxo, já que agora ele tem que fazer algo notoriamente significativo com cada elemento do fluxo.
fonte