Em Java, quais são as vantagens dos fluxos sobre os loops? [fechadas]

133

Me perguntaram isso em uma entrevista e não estou convencido de ter dado a melhor resposta que poderia ter. Mencionei que você pode fazer uma pesquisa paralela e que os valores nulos foram tratados por alguns meios que eu não conseguia lembrar. Agora percebo que estava pensando em opcionais. O que estou perdendo aqui? Eles afirmam que é um código melhor ou mais conciso, mas não tenho certeza se concordo.


Considerando o quão sucintamente foi respondida, parece que essa não era uma pergunta muito ampla, afinal.


Se eles estão fazendo essa pergunta em entrevistas, e claramente estão, que objetivo poderia ser útil, a não ser tornar mais difícil encontrar uma resposta? Quero dizer, o que você está procurando? Eu poderia quebrar a pergunta e ter todas as sub-perguntas respondidas, mas criar uma pergunta dos pais com links para todas as subquestões ... parece bem boba. Enquanto estamos nisso, por favor, dê-me um exemplo de uma pergunta menos ampla. Não conheço nenhuma maneira de fazer apenas parte dessa pergunta e ainda assim obter uma resposta significativa. Eu poderia fazer exatamente a mesma pergunta de uma maneira diferente. Por exemplo, eu poderia perguntar "Para que finalidade os fluxos servem?" ou "Quando eu usaria um fluxo em vez de um loop for?" ou "Por que se preocupar com fluxos em vez de com loops?" Porém, essas são exatamente a mesma pergunta.

... ou é considerado muito amplo porque alguém deu uma resposta multiponto realmente longa? Francamente, qualquer pessoa que sabe pode fazer isso com praticamente qualquer pergunta. Se você é um dos autores da JVM, por exemplo, provavelmente poderia falar sobre loops o dia inteiro, quando a maioria de nós não poderia.

"Edite a pergunta para limitá-la a um problema específico com detalhes suficientes para identificar uma resposta adequada. Evite fazer várias perguntas distintas ao mesmo tempo. Consulte a página Como pedir ajuda para esclarecer esta pergunta."

Conforme observado abaixo, foi dada uma resposta adequada que comprova que existe uma e que é fácil de fornecer.

user447607
fonte
7
Isso é baseado na opinião. Pessoalmente, prefiro fluxos porque torna o código mais legível. Permite escrever o que você deseja, em vez de como . Além disso, é totalmente foda fazer coisas incríveis com one-liners.
Arnaud Denoyelle 25/05
19
Mesmo se for uma linha de 30, um forro? Não gosto de longas correntes.
User447607
1
Além disso, tudo o que estou procurando aqui é a resposta apropriada para uma entrevista. Esta é a única "opinião" que importa.
User447607
1
Educacionalmente falando, essa pergunta também me salvou de degradação em uma entrevista futura, @slim realmente acertou em cheio, mas Industrialmente, também fala de como as linguagens de programação da Microsoft construíram suas carreiras para arrancar a linguagem java e, finalmente, a java consegue vingança rasgando fora da expressão lambda e fluxos dos adversários, vamos ver o que java vai fazer sobre structs e uniões no futuro :)
ShayHaned
5
Note-se que fluxos única tocar uma fração do poder na programação funcional: - /
Thorbjørn Ravn Andersen

Respostas:

251

Interessante que a pergunta da entrevista pergunte sobre as vantagens, sem perguntar sobre as desvantagens, pois existem as duas.

Os fluxos são um estilo mais declarativo . Ou um estilo mais expressivo . Pode ser considerado melhor declarar sua intenção no código, do que descrever como isso é feito:

 return people
     .filter( p -> p.age() < 19)
     .collect(toList());

... diz claramente que você está filtrando elementos correspondentes de uma lista, enquanto:

 List<Person> filtered = new ArrayList<>();
 for(Person p : people) {
     if(p.age() < 19) {
         filtered.add(p);
     }
 }
 return filtered;

Diz "Estou fazendo um loop". O objetivo do loop está enterrado mais profundamente na lógica.

Os fluxos geralmente são mais tersos . O mesmo exemplo mostra isso. Terser nem sempre é melhor, mas se você pode ser conciso e expressivo ao mesmo tempo, tanto melhor.

Os fluxos têm uma forte afinidade com as funções . O Java 8 apresenta lambdas e interfaces funcionais, que abrem toda uma caixa de brinquedos com técnicas poderosas. Os fluxos fornecem a maneira mais conveniente e natural de aplicar funções a sequências de objetos.

Os fluxos incentivam menos mutabilidade . Isso está relacionado ao aspecto da programação funcional - o tipo de programa que você escreve usando fluxos costuma ser o tipo de programa em que você não modifica objetos.

Os fluxos incentivam o acoplamento mais flexível . Seu código de manipulação de fluxo não precisa saber a origem do fluxo ou seu eventual método de encerramento.

Os fluxos podem expressar sucintamente um comportamento bastante sofisticado . Por exemplo:

 stream.filter(myfilter).findFirst();

Pode olhar à primeira vista como se filtra todo o fluxo e depois retorna o primeiro elemento. Mas, de fato, findFirst()dirige toda a operação e, portanto, pára de maneira eficiente depois de encontrar um item.

Os fluxos fornecem espaço para futuros ganhos de eficiência . Algumas pessoas compararam e descobriram que fluxos de thread único de Lists ou matrizes na memória podem ser mais lentos que o loop equivalente. Isso é plausível porque há mais objetos e despesas gerais em jogo.

Mas os fluxos escalam. Assim como o suporte interno do Java para operações de fluxo paralelo, existem algumas bibliotecas para redução de mapa distribuído usando o Streams como a API, porque o modelo se encaixa.

Desvantagens?

Desempenho : um forloop através de uma matriz é extremamente leve, tanto em termos de heap quanto de uso da CPU. Se a velocidade bruta e a economia de memória são uma prioridade, o uso de um fluxo é pior.

Familiaridade . O mundo está cheio de programadores procedimentais experientes, de várias origens linguísticas, para quem os loops são familiares e os fluxos são novos. Em alguns ambientes, você deseja escrever um código familiar para esse tipo de pessoa.

Sobrecarga cognitiva . Devido à sua natureza declarativa e ao aumento da abstração do que está acontecendo abaixo, você pode precisar criar um novo modelo mental de como o código se relaciona com a execução. Na verdade, você só precisa fazer isso quando as coisas derem errado ou se precisar analisar profundamente o desempenho ou erros sutis. Quando "simplesmente funciona", simplesmente funciona.

Os depuradores estão melhorando, mas mesmo agora, quando você está percorrendo o código de fluxo em um depurador, pode ser um trabalho mais difícil que o loop equivalente, porque um loop simples está muito próximo das variáveis ​​e locais de código com os quais um depurador tradicional trabalha.

fino
fonte
4
Eu acho que seria justo explicar que coisas do tipo stream estão se tornando muito mais comuns e agora aparecem em muitas linguagens comumente usadas que não são especialmente orientadas para FP.
Casey
5
Dados os prós e os contras listados aqui, acho que os fluxos NÃO valem a pena para nada além de usos muito simples (pouca lógica se / então / então, não há muitas chamadas aninhadas ou lambdas etc.), em partes de desempenho não críticas
Henrik Kjus Alstad
1
@HenrikKjusAlstad Esse não é absolutamente o assunto que eu pretendia comunicar. Os fluxos são maduros, poderosos, expressivos e completamente apropriados para o código de nível de produção.
magro
Oh, eu não quis dizer que não usaria na produção. Mas preferiria usar loops / ifs à moda antiga etc, em vez de fluxos, especialmente se o fluxo resultante parecer complexo. Tenho certeza de que há usos em que um fluxo supera loops e se é claro, mas, na maioria das vezes, acho que está "empatado", ou às vezes até o contrário. Assim, eu colocaria peso no argumento da sobrecarga cognitiva por seguir as velhas formas.
Henrik Kjus Alstad 26/11
1
@ lijepdam - mas você ainda teria um código que diz "Estou iterando nesta lista (veja dentro do loop para descobrir o porquê)", quando "iterar sobre a lista" não é a intenção principal do código.
slim
16

Divertido sintático à parte, os Streams são projetados para trabalhar com conjuntos de dados potencialmente infinitamente grandes, enquanto matrizes, Coleções e quase todas as classes Java SE que implementam o Iterable estão inteiramente na memória.

Uma desvantagem de um fluxo é que filtros, mapeamentos etc. não podem gerar exceções verificadas. Isso faz do Stream uma péssima escolha para, digamos, operações de E / S intermediárias.

VGR
fonte
7
Obviamente, você também pode percorrer infinitas fontes.
magro
Mas se os elementos a serem processados ​​persistirem em um banco de dados, como você usa o Streams? Um desenvolvedor júnior pode ficar tentado a ler todos eles em uma coleção apenas para usar o Streams. E isso seria um desastre.
Lluis Martinez
2
@LluisMartinez, uma boa biblioteca cliente de banco de dados retornará algo como Stream<Row>- ou seria possível escrever as próprias Streamoperações de cursor de resultado de banco de empacotamento de implementação.
magro
O fato de o Streams ignorar silenciosamente as exceções é um erro pelo qual fui mordido recentemente. Não intuitivo.
Xxfelixxx
@xxfelixxx Os fluxos não ignoram silenciosamente as exceções. Tente executar o seguinte:Arrays.asList("test", null).stream().forEach(s -> System.out.println(s.length()));
VGR
8
  1. Você percebeu incorretamente: operações paralelas usam Streams, não Optionals.

  2. Você pode definir métodos para trabalhar com fluxos: tomando-os como parâmetros, retornando-os etc. Você não pode definir um método que usa um loop como parâmetro. Isso permite uma operação de fluxo complicada uma vez e a utiliza várias vezes. Observe que o Java tem uma desvantagem aqui: seus métodos devem ser chamados em someMethod(stream)oposição aos do próprio fluxo stream.someMethod(), portanto, misturá-los complica a leitura: tente ver a ordem das operações em

    myMethod2(myMethod(stream.transform(...)).filter(...))

    Muitos outros idiomas (C #, Kotlin, Scala, etc) permitem alguma forma de "métodos de extensão".

  3. Mesmo quando você precisa apenas de operações sequenciais e não deseja reutilizá-las, para poder usar fluxos ou loops, operações simples nos fluxos podem corresponder a alterações bastante complexas nos loops.

Alexey Romanov
fonte
Explicar 1. A interface opcional não é o meio pelo qual os nulos são manipulados em cadeias? Em relação a 3, isso faz sentido porque, com filtros em curto-circuito, o método será invocado apenas para ocorrências especificadas. Eficiente. Faz sentido que eu poderia afirmar que a sua utilização reduz a necessidade de escrever código adicional que terá de ser testado etc. Após a revisão, eu não tenho certeza do que você quer dizer com o caso sequencial no 2.
user447607
1. Optionalé uma alternativa null, mas não tem nada a ver com operações paralelas. A menos que "agora percebo que estava pensando em opcionais" em sua pergunta, esteja falando apenas sobre nullmanuseio?
Alexey Romanov
Eu mudei a ordem de 2 e 3 e ampliei os dois um pouco.
Alexey Romanov
6

Você faz um loop sobre uma sequência (matriz, coleção, entrada, ...) porque deseja aplicar alguma função aos elementos da sequência.

Os fluxos oferecem a capacidade de compor funções em elementos de sequência e permitem implementar as funções mais comuns (por exemplo, mapeamento, filtragem, localização, classificação, coleta, ...) independentemente de um caso concreto.

Portanto, dada a tarefa de loop na maioria dos casos, você pode expressá-la com menos código usando o Streams, ou seja, você ganha legibilidade .

wero
fonte
4
Bem, não é apenas legibilidade. O código que você não precisa escrever é o código que você não precisa testar.
User447607
3
Parece que você está chegando com boas respostas para a sua entrevista também
wero
6

Eu diria que sua paralelização é tão fácil de usar. Tente iterar milhões de entradas em paralelo com um loop for. Vamos a muitos cpus, não mais rápidos; então, quanto mais fácil for correr paralelamente, melhor e com Streams isso é fácil.

O que eu gosto muito é a verbosidade que eles oferecem. Leva pouco tempo para entender o que eles realmente fazem e produzem, em vez de como eles fazem isso.

Eugene
fonte