Transcrições
1. Apresentação: Olá pessoal,
bem-vindos à aula Java eight streams, uma introdução
prática. Meu nome é viajante
e, nesta aula,
contarei tudo sobre o uso
de streams. Esta aula tem como objetivo dosar, ter pouca ou nenhuma
experiência com riachos é gaze para lhe dar uma
visão sobre a possibilidade de riachos. Você reconhece situações em
que os streams podem ser úteis. Vamos nos aprofundar no uso e em geral dos
streams. Você quer usá-los em interfaces
funcionais
e lambdas. Vou explicar as operações comumente
usadas para aqueles que desejam uma abordagem
mais prática. Este curso também
oferece exercícios para colocar em prática o conhecimento
aprendido. Vamos começar.
2. Uso do fluxo e caso de uso: Uso e caso de uso de streams. Os fluxos de água usam
, simplesmente, um riacho pode ser usado para
processar uma coleção de itens. Em um capítulo posterior, vamos nos aprofundar em algumas
das formas específicas de
processar uma coleção. Mas, por enquanto, pense no
processamento como filtragem, alteração e enriquecimento
dos dados para coleta. Observe, no entanto, que
as produções usadas como entrada para streams não são
realmente alteradas pelas transmissões. Em vez disso, a execução de
um stream nos dá resultados
separados, desconectados da coleção original. Essas etapas
de processamento de operações são escritas de forma funcionalmente
descritiva. Ou seja, um stream parece
uma história quando feito corretamente, o
que o torna
incrivelmente útil. Para mostrar isso. Aqui está um exemplo de uma forma
convencional de filtrar, coletar, classificar
e imprimir alguns dados. Eu uso a mesma lógica
escrita em streams. Embora eu tenha certeza de que você será capaz entender
os dois lados eventualmente, acho que todos podemos concordar que o uso de streams torna muito, muito mais fácil entender
o que o código faz. Então aí está.
Os fluxos são usados para processar uma coleção de itens
de uma forma bastante razoável. Como os streams funcionam? Como acabamos de dizer, os streams funcionam
em uma coleção de itens. Você deve ter percebido que a coleção de palavras
corresponde a uma interface. Em Java. interface encontrada
no pacote Java util é nossa vinculada para trabalhar
com streams. Na maioria das vezes, você costuma usar uma
lista ou conjunto para iniciar um fluxo que estende a interface de
coleta. Há outras formas
de criar um stream. Não vamos falar sobre
eles nesta aula. Você pode encontrá-los
nos recursos. Com a chegada do Java 8, um método padrão chamado stream foi adicionado
à interface de coleta. Para quem não está familiarizado
com os métodos padrão. Os métodos padrão
permitem que você adicione novas funcionalidades às
interfaces de suas bibliotecas e assegure a compatibilidade
binária com o código escrito para
versões mais antigas das interfaces. Esse fluxo de método padrão retorna uma instância
do stream custa a classe central da API que nos permite
trabalhar com eles. Depois de criar o stream, agora
você pode encadear as operações e executar o stream. Mais sobre isso mais tarde. Vamos resumir o que
aprendemos até agora. Os fluxos são usados para processar uma coleção de itens de uma
forma muito legível. Os streams não alteram
essa coleção de origem, mas criam
um resultado separado desconectado da coleção
original. A interface de stream é o custo central da API de
trabalhar com streams. Qualquer coisa que estenda
ou implemente a interface de coleta tem um método de stream padrão para interagir com a classe
central da API de conjuntos.
3. Interfaces funcionais e Lambda: Interfaces funcionais
e lambdas. Antes de começarmos a falar
sobre as operações de streaming, precisaremos falar
sobre os pré-requisitos do uso de faixas. As lambdas. Lambdas são basicamente classes
internas anônimas, interfaces
funcionais das quais o compilador Java implícito indica as informações necessárias. Vamos começar do começo. Classes internas anônimas. Ao implementar uma interface, você implementará
todos os seus métodos. Aqui está um exemplo
em que você cria classe
implementada
e a instancia. Aqui temos uma interface chamada minha interface
com a impressão. Algo importante é implementado pela minha interface. Mas e se eu precisar de uma implementação ligeiramente
diferente das impressões, um método
importante. Nesse cenário, eu
teria que criar outra classe que
implementasse minha interface. Novamente. Imagine se você tiver
dez variações diferentes, elas ficarão
confusas em breve. Essa é uma dica para classes internas
anônimas. classe interna anônima é
uma extensão de uma classe. Uma implementação
de uma interface sem nome, portanto anônima. Vamos nos concentrar na parte S da
interface que é a mais importante
no contexto dos fluxos. Como eles não têm nome, não
podemos criar sozinhos uma instância da
classe interna anônima. Em vez disso, você precisa declarar e instanciar a classe interna
anônima em uma única expressão. Essa expressão
tem a seguinte aparência, que
se traduz no seguinte. Ao criar a versão de classe
interna anônima das informações da minha interface. Como você pode ver,
criamos uma instância da minha interface sem precisar uma classe implementando
a interface. E se eu quiser adicionar uma nova implementação da minha interface, posso simplesmente fazer isso.
Aí está. Classes internas anônimas
podem ser usadas para implementar uma interface sem
a necessidade de definir uma classe rígida. Você pode simplesmente
criá-los em tempo real. Interfaces funcionais. A interface funcional é apenas uma interface , exceto que ela contém apenas um único método que
deve ser implementado. Qualquer interface que atenda a esses critérios é uma interface
funcional. Assim como nosso
exemplo de minha interface agora. Embora não seja necessário, você pode adicionar a
anotação funcional da interface para mostrar sua intenção. Como um bônus adicional, o compilador lançará uma exceção em tempo de compilação
quando não for realmente uma interface funcional ao usar essa anotação. Vamos analisar as interfaces funcionais mais
comuns usadas ao trabalhar com streams. primeiro é o
fornecedor com receitas admitidas. Ele não espera nenhuma discussão, mas retorna alguma coisa. O fornecedor
fornece algo, geralmente um objeto novo,
vazio e limpo. Você poderia ver isso como uma fábrica. O próximo é o
predicado. Com admitido. Ele espera um argumento, o avalia e
retorna um booleano. Como o nome indica, é basicamente um teste. O argumento para atender aos critérios
fornecidos é sim ou não? consumidor com seus
métodos aceita, aceita um argumento,
faz algo com ele, mas não retorna nada, literalmente consumindo os
argumentos do processo. O consumidor de bicicletas faz
o mesmo que o consumidor, exceto que espera dois argumentos. A função com seus métodos se aplica. Espera uma discussão,
faz algo com ela. E os retornos em argumentos estão basicamente mapeando ou enriquecendo o argumento no processo, os argumentos r e t
podem ser da mesma classe. A função by faz
o mesmo que a função. Exceto que espera dois argumentos, T e U fazem
algo com eles. Seus retornos são exatamente
como a função. Os argumentos podem ser da
mesma classe, se necessário. E nessa nota, chegamos
à última interface
funcional comum. Operador binário. O operador binário é uma extensão
de por função em que T, U e R são
exatamente a mesma classe. Certifique-se de ter uma compreensão
decente das interfaces funcionais em geral e dessas interfaces
comuns antes de continuar. Pois isso ajudará você a entender lambdas e transmitir
operações com mais facilidade. Expressões lambda. mais confuso somos classes internas
anônimas de interfaces
funcionais, das quais o compilador Java
conhece implicitamente as informações necessárias. Agora sabemos o que
é uma classe interna
anônima e como ainda precisamos implementar todos os métodos
de uma interface. Percebemos que
as interfaces funcionais são interfaces regulares, exceto que elas têm apenas um único método que
deve ser implementado. Como uma interface funcional tem
apenas um único método, o compilador é
capaz de entender muitas informações
com base no contexto. Você, como desenvolvedor, pode
omitir essas informações. Então, informe ao compilador que
você está omitindo coisas. Os problemas de sintaxe do Lambda. Começaremos com uma classe interna
anônima irregular e a
converteremos em uma expressão
lambda completa ,
peça por peça. Ou use o predicado da
interface funcional para testar se um determinado número inteiro
é maior que um. Observando o sinal de igual, notamos o nome da
interface que estamos implementando. O corpo da classe interna
anônima, a assinatura do método e o corpo dos métodos
que estamos implementando. Examine mais de perto o nome da
interface e a assinatura do método. Você vê como o compilador
poderia inferir esse conhecimento? Observe como os
nomes das interfaces já estão no lado esquerdo
das declarações iguais. Observe que, como o predicado
é uma interface funcional, há apenas um único
método a ser implementado. Então, já sabemos quais
métodos estamos implementando. Esses dois tipos de informações podem ser
inferidos pelo compilador. Nosso primeiro passo é escrever isso usando a
sintaxe agrupada, a seta. Com isso. Vamos nos livrar do nome da
interface, do nome do método. Isso já parece
muito mais limpo, certo? Aqui vemos a entrada
dos métodos, o número inteiro no
lado esquerdo da seta e o corpo do
método no lado direito. Mas ainda há
algumas informações que podemos omitir neste caso. Como há um
único método e o compilador sabe
a assinatura dele, o compilador pode inferir o tipo de
entrada a partir do contexto. Os colchetes ao redor das entradas também
desapareceram. Isso só é possível
porque é um único parâmetro. Caso você tenha dois ou mais, os colchetes são obrigatórios. Há mais uma coisa que
podemos inferir agora. Tem a ver com o corpo
e a declaração de devolução. No caso de ter uma única linha de código no corpo
do método,
o compilador pode inferir
que essa linha de código é de fato a
declaração de retorno do corpo. Com essa etapa final, chegamos à expressão
lambda completa. Você verá o tempo todo
ao trabalhar com cordas. Na verdade, há uma maneira de
escrever o lambda ainda mais curto. Em alguns casos. Isso é chamado de referência de método. Mas eu sugiro
que você mesmo pesquise quando tiver uma boa compreensão de
doses longas em geral. Uma última coisa importante a mencionar sobre nós agrupados é que as variáveis
locais usadas neles devem ser finais ou
efetivamente finais. Efetivamente final
significa uma variável que não tem
a palavra-chave final, mas seu valor não muda
após sua primeira atribuição. A razão para isso é que nós
agrupados somos basicamente um pedaço de código que você pode
executar em outros métodos. como você pode
passar nosso predicado para uma operação de filtro
dentro de uma string. Por esse motivo, o
Java precisa criar uma cópia da variável local
para ser usada nos pulmões. Para evitar problemas de simultaneidade. O alfa decidiu
restringir completamente
as variáveis locais. Vamos resumir o que
aprendemos até agora. Classes internas anônimas
são classes internas que implementam uma interface
sem ter um nome, precisam ser declaradas
e instanciadas dessa vez. As interfaces funcionais são interfaces regulares , exceto que contêm apenas um único método que
deve ser implementado. A anotação da
interface funcional pode ser usada para impor
essa regra, não é necessária. agrupados são classes
internas anônimas de
interfaces funcionais das quais o compilador Java pode inferir informações
ausentes
com base no contexto. Agora sabemos como criar uma expressão lambda e de classe interna anônima
irregular. E, por fim, as variáveis locais usadas nos agrupados devem ser
definitivas ou efetivamente corretas.
4. Operações de fluxo: Nos próximos capítulos, explicarei
as operações mais usadas em cadeias de caracteres. Mas primeiro, devemos falar sobre os dois tipos de operações
possíveis, operações intermediárias e de
terminal. Um stream só é totalmente executado quando uma
operação de terminal é usada. Portanto, um fluxo
sempre termina em uma operação de terminal e só pode ter um
deles em um fluxo. As operações intermediárias
são todas as operações antes de chegarmos à operação final
do terminal. As operações intermediárias
retornam uma instância de fluxo, possibilitando
encadeá-las todas juntas. Como todas as
operações intermediárias retornam fluxo, você pode armazenar uma string sem operações de
terminal
em uma variável. Obrigado hoje, então você pode
realmente adicionar etapas à sua transmissão com base em influências
externas, como um filtro diferente ou uma forma diferente de
enriquecer os dados. No entanto, preste atenção,
mesmo que você possa armazenar
operações intermediárias em variáveis, não
é possível executar o mesmo fluxo duas vezes com
uma operação de terminal. O compilador não
reconhece isso, mas você receberá uma exceção
como essa durante o tempo de execução. Mais uma vez. As operações intermediárias
retornam um fluxo e, portanto, podem ser vinculadas
e armazenadas em variáveis. As operações intermediárias sozinhas
não executam um stream. Uma operação de terminal executa o fluxo completo e retorna
um valor diferente de um fluxo. Um stream só pode ser executado uma vez com uma operação de
terminal. Tentar fazer isso duas vezes resulta em uma exceção
durante o tempo de execução. Com esse conhecimento,
continuaremos com
as operações mais usadas.
5. Operação intermediária: filtro: Vamos começar com o básico. Filtro. O filtro é uma
operação intermediária que permite filtrar objetos de
cadeias de caracteres que não
passam no teste especificado. Você pode ver o filtro de operação
intermediário como o equivalente
de fluxo de uma instrução if. entrada é um predicado que, como aprendemos,
recebe um objeto, realiza um teste nele, retorna verdadeiro ou falso dependendo se ele
passou no teste ou não. Aqui está um exemplo de seu uso. E não se preocupe com
as outras operações. Chegaremos a eles em breve. Neste exemplo,
usamos o filtro para continuar o fluxo somente com objetos cuja lista de
números de telefone esteja vazia. Antes de retornar aos
clientes restantes como uma lista, filtragem pode ser feita usando
qualquer valor final efetivo. Aqui, estamos filtrando com
base em nossa variável local, por exemplo, também
podemos
encadeá-las usando vários
filtros em uma linha. A operação do filtro é um excelente exemplo de como os fluxos tornam seu
código mais legível. Especialmente se você armazenou
por muito tempo em um valor como esse. Agora você pode ver o que estamos
filtrando em um piscar de olhos.
6. Operação de terminais: forEach: Nossa primeira
operação de terminal para cada um. O forEach é uma operação de terminal muito
simples. É usado para realizar uma determinada ação em todos os
elementos do fluxo. Supondo que
os elementos do processo, suas entradas, seja um consumidor. Então, ele pega um objeto, faz algo com ele, mas não retorna nenhum valor. Depois. Vimos
sua implementação na operação
discutida anteriormente, mas aqui está mais uma vez. Neste exemplo, imprimimos o primeiro nome de cada setor
cliente. Lembre-se de que
cada um deles só opere
nos elementos depois que eles
passarem pelo
resto do fluxo. Portanto, se colocarmos um filtro antes
da operação do terminal, dessa forma, somente clientes com menos de três dispositivos
obterão seu primeiro nome e o imprimirão. Finalmente, ela sabia que um método normal também pode ser
usado em um grumado? Use esse método, por exemplo isso
não parece uma implementação de
consumidor para você? Ele aceita um único objeto, faz algo com ele, mas não retorna
nenhum valor depois. E, de fato, podemos usar esse
método em nosso ForEach, tornando-o mais legível
no processo. E é isso. Para cada
um, simplesmente executa uma ação específica para cada elemento do fluxo que passa
pelo resto do fluxo.
7. Operação intermediária: mapa: Outra
operação intermediária básica, o mapa. O mapa é uma
operação intermediária comumente usada para mapear de um tipo
de objeto para outro. Mas normalmente também é usado
para realizar a lógica de negócios. Suas entradas são uma função
que, como sabemos agora, pega um objeto da classe a, faz algo com ele e retorna um objeto da classe b onde as cláusulas a e
B podem ser a mesma classe. Aqui está um exemplo em que
mapeamos da classe a para a B. Aqui temos um
objeto de cliente como entradas. Mapeamos isso para uma
sequência composta por seu nome e
sobrenome antes de imprimi-la. Nesse caso, estamos mapeando
da classe a para a classe B, do cliente para a string. No próximo exemplo, temos um objeto de cliente como entrada móvel no trabalho
para seus dispositivos. Em seguida, devolva o mesmo objeto
do cliente, enriquecendo
efetivamente
o próprio objeto. Nesse caso, estamos
mapeando de cliente para
cliente e alcançando o
objeto ao longo do caminho. Observe que, como o corpo do Lambda contém
duas declarações, tive que adicionar os colchetes
e retornar a palavra-chave. Finalmente, assim como em qualquer operação
intermediária, é possível ter várias operações matemáticas
dentro do mesmo fluxo.
8. Operação intermediária: flatMap: Em seguida, FlatMap. Como o nome sugere, FlatMap é semelhante ao map. É uma
operação intermediária comumente usada para mapear de um tipo
de objeto para outro. Com a diferença de que FlatMap funciona em relações
um-para-muitos. Sua entrada também é uma função, mas uma restrição é
imposta ao objeto de retorno. Um stream deve ser retornado. Aqui vemos a
assinatura do método map e FlatMap lado a lado para
mostrar a diferença. Embora ambos esperem a função, FlatMap espera que o valor
r seja do tipo string. Vamos dar uma olhada no que ele faz e como ele
difere disso. Em nossos exemplos,
usamos o objeto cliente, que tem uma lista de dispositivos. O que você acha que
aconteceria se usássemos
a operação do mapa para converter cada cliente
em seus dispositivos. Como você pode ver na saída, o mapeamento de um cliente
para uma lista de dispositivos resulta em um fluxo
de listas de dispositivos. Quando impressas, essas listas
são impressas uma a uma. Isso pode ser algo que você precisa para seu caso de uso funcional. Mas, na maioria das vezes, você está interessado em
todos os dispositivos separados em vez de
listas separadas de dispositivos. É aqui que entra o FlatMap. Como disse antes. Flatmap impõe uma restrição ao objeto
de retorno da função. Você pode ver que o lambda
mudou ligeiramente para FlatMap e agora retorna
o dispositivo como um stream. Em vez de uma lista. Observe nas saídas
que os dispositivos agora são impressos um por um,
em vez de lista por lista. A chave aqui é que o
FlatMap espera fluxos como resultados que são então achatados em um
único fluxo novamente. Mais uma vez juntos. Agora você pode
ver claramente a diferença entre o mapa e a operação
FlatMap. Para resumir, os mapas
devem ser usados ao converter objetos
com uma relação de um para um. Já o FlatMap
deve ser usado ao converter objetos com
uma relação de um para muitos. Ao usar flatMap, o valor de retorno é
um fluxo de objetos.
9. Operações intermediárias: sequencial e paralelo: Duas operações da mesma
moeda, sequenciais e paralelas. Sequencial e paralelo são
operações intermediárias que tornam o fluxo completo sequencial ou paralelo
, respectivamente. Como são operações
intermediárias, elas podem ser adicionadas ao stream juntas até mesmo várias vezes. Não importa onde a
operação é colocada no fluxo. O fluxo inteiro é
sequencial ou paralelo. Nem um pouco dos dois. Lembre-se de que a última operação sequencial
ou paralela mencionou
a indústria, aquela que é realmente usada. Isso significa que esse fluxo é exatamente o
mesmo que esse fluxo. Um fluxo é sequencial
por padrão, o
que significa que os elementos
são executados
na ordem em que aparecem
na coleção dos EUA. Isso também significa que
esses fluxos têm a mesma saída. Como você pode ver. Isso resulta na impressão
de um a dez em ordem com ou sem a operação
sequencial. Quando mudamos isso para paralelo, no entanto, como você pode ver, obtemos um resultado diferente
toda vez que executamos o Stream. O stream é executado em
vários segmentos, o que
significa que o trabalho realizado é dividido entre
diferentes execuções. As chamadas ameaças. Lembre-se de que fazer com que
um fluxo seja executado em paralelo não
significa automaticamente que o fluxo possa ser executado com segurança de encadeamento. Você, como desenvolvedor, precisa garantir que seu
código funcione exatamente da mesma forma, 1236 ou qualquer outra ameaça. Como não quer. Outra coisa a
ter em mente ao executar as coisas em paralelo é que isso não significa
necessariamente que o
trabalho seja feito mais rápido. Criar algo
multiencadeado significa que você precisa dividir
o trabalho em partes, criar uma execução
para cada peça. Trabalho delegado, reúna os
diferentes resultados em um. Isso resulta em
alguma sobrecarga ao executar o código ou
stream, no nosso caso, o que só será benéfico se você tiver elementos suficientes em seu stream ou se o stream
for muito pesado em computação. Para resumir, as operações sequenciais
e paralelas permitem que você torne rapidamente todo
o fluxo
sequencial ou paralelo. executar um fluxo paralelo, você, como desenvolvedor, precisa garantir que o código
seja seguro para encadeamentos.
10. Operação intermediária: Operation:: Vamos usar um
simples antes de passarmos para os complexos. Pq. Pq é uma operação
intermediária que literalmente permite que
você dê uma olhada nos
bastidores ao
executar uma string. Seu principal uso é depurar
e registrar suas entradas. Como consumidor,
o que significa que ele recebe um valor e faz algo com ele sem que haja
um valor de retorno. Embora seja possível modificar os dados dentro da operação de pico, você corre o risco de fazer isso. Dependendo da sua
versão do Java e da necessidade ou não sua operação de terminal de
sua operação de terminal processar os
objetos na string. O código dentro do peek
pode ou não ser executado. Tenha muito cuidado
ao usar o peak para qualquer coisa que não
seja depuração e registro. Aqui está um exemplo de seu uso. Neste exemplo, usamos o
peak para imprimir o nome de
um dispositivo antes de continuarmos mapeando-o para os detalhes do pedido, coletando esses possíveis processamentos
adicionais. Um resultado desse fluxo
é uma lista de detalhes do pedido. Graças ao pico de operação, podemos obter alguns registros com um dispositivo que os objetos usam
para os resultados. E eu vi que o pico é simplesmente uma maneira de obter um pouco de
registro durante um fluxo. Você pode usá-lo para modificar
dados, mas é arriscado, então eu não recomendaria isso quando você ainda não estiver
familiarizado com streams.
11. Operação de terminais: coletar: Nossa segunda
operação de terminal, coleta. Collect é uma
operação de terminal que coleta todos os itens de
um stream em algo. É comumente usado para colocar os itens resultantes de
um stream em uma lista. Há duas implementações
para essa. O primeiro espera
uma implementação da interface do
coletor. No entanto, essa não é uma
interface funcional
e, portanto, não pode ser reescrita
como uma expressão lambda. Felizmente para nós, sim, Lava teve a
gentileza de fornecer grupos de coletores contendo todos os tipos de métodos úteis. Muitos estão além do
escopo deste workshop. Mas há uma
que você
usará com frequência : listas de pontos dois de coletores. Como você poderia esperar. Isso retorna uma implementação da interface do coletor que coleta todos os elementos
da string em uma
lista e a retorna. Eu pessoalmente uso isso 190, 9% das vezes que
preciso coletar algo e espero que você
experimente o mesmo. Mas, para entender um pouco melhor o
que ele faz, vamos analisar a
segunda implementação. Este espera um fornecedor
e dois dos consumidores. O documento Java nos dá uma boa
visão geral do que ele faz. O fornecedor
nos fornece isso, o que esperamos como resultado
da operação de coleta. Do que o primeiro
consumidor de bicicleta está acostumado a consumir um elemento do
fluxo para obter esses resultados. Isso continua até que
todos os elementos
do fluxo ou sejam consumidos
até os resultados finais. Isso não nos mostra o que o segundo consumidor bi
a combiná-lo faz, no entanto, isso
ocorre porque o combinador só
é necessário quando o
fluxo é executado em paralelo. Nesse caso, você pode ter dois ou mais tópicos
começando com o fornecedor. Depois que todas as ameaças forem
concluídas, os resultados desses
encadeamentos devem ser combinados em um único resultado, que é exatamente para o qual um
combinador é usado. Vamos colocar isso em prática. Usando um fornecedor e
dois pelos consumidores. Vou replicar a coleta dos elementos do cliente
em um fluxo. Começaremos com o fornecedor. Quando as chamadas obterão a lista vazia na qual colocaremos todos os elementos do stream. Em seguida, precisamos de um consumidor de
bicicletas que acumule todos os elementos
da corda na lista de suprimentos. Isso deixa claro que nosso resultado é realmente
essa lista e que todos os elementos do cliente
do fluxo são acumulados
em uma lista definida. No caso de um fluxo sequencial, isso será o fim dele. Mas para permitir o processamento
paralelo, será necessário um final por consumidor que combine dois
resultados em um. Usando isso por consumidor, adicionamos os elementos
do segundo resultado
ao primeiro. Isso é
repetido pelo stream até que todos os resultados das ameaças sejam
mesclados novamente em um. Aqui eles são
preenchidos na operação de coleta. E com isso, você sabe como personalizar
suas próprias coleções. Para reiterar, existem duas implementações
da cooperação de coleta. O primeiro espera
um coletor do qual o Java preparou algumas opções para você usando a cláusula
collectors. O segundo espera um
fornecedor e vincule os consumidores, tornando-o muito
mais personalizável. Você usará a aula preparada para coletar esta aula na
maioria das vezes. Mas pelo menos agora você
sabe como isso funciona.
12. Operação de terminais: reduza: O operador final
discutirá a redução. Reduzir é uma
operação de terminal que pode ser usada para reduzir todos os elementos de um
fluxo em um único objeto. É um pouco como a
operação de coleta que discutimos. Mas o collect
aplica todos os elementos do fluxo
a um
contêiner imutável, como uma lista ou conjunto. O Reduce não
coleta, mas aplica todos os elementos a um único
objeto. A identidade. Reduza, reúne sua identidade, acumule elementos ou elementos
na identidade e, em
seguida, combina vários resultados em um,
caso seja executado em paralelo. Reduzir é a
operação mais difícil de entender. Portanto, não se sinta mal se
não conseguir tudo de uma vez. Basta
seguir esta seção e experimentar os exercícios
no final da aula. Se você ainda tiver alguma dúvida, sinta-se à vontade para perguntar
usando a plataforma. Tudo bem, vamos mergulhar. redução profunda espera
um valor inicial chamado A redução profunda espera
um valor inicial chamado
função de identidade que é usado como acumulador e
um operador binário usado para combinar resultados
de segmentos separados. O cão alfa nos dá uma boa
visão geral do que ele faz. Essa identidade nos dá
o início dos resultados. Deve ser uma folha limpa e vazia, forma que adicionar qualquer
valor do fluxo a essa identidade tenha esse resultado
e seja alterado como produto. Então, para cada elemento
do fluxo, ele é acumulado
no valor resultante
usando a função by. Assim como com coletar, isso não nos mostra o que
o operador binário,
o combinador, faz. O mesmo se aplica aqui. O combinador só é usado
para combinar os resultados de vários encadeamentos,
caso o fluxo seja executado paralelamente. Vamos colocar isso em prática. nosso exemplo,
escolheremos todos os
clientes em um número inteiro representando a quantidade total de
dispositivos de propriedade desses
clientes juntos. Nossa identidade deve
ser um número inteiro. Como resultado, queremos
sair desse fluxo. No que diz respeito ao valor. Lembre-se de que adicionar
qualquer resultado
à identidade deve
resultar na alteração do produto. No nosso caso, teremos que
adicionar o tamanho de toda a lista de
dispositivos para chegar
aos resultados finais. Qual valor da
identidade significa que identidade mais o tamanho da lista de
dispositivos é igual a dois. O dispositivo é,
isso mesmo, zero. Então, nossa identidade
será assim. Em seguida, precisamos de uma função que produza um elemento do
fluxo nessa identidade. O primeiro genérico usado
na função by é um número inteiro que representa o valor atual
dos resultados. O subtotal, se você quiser. O segundo uso de genéricos é
o elemento do fluxo, que
se reduzirá ao subtotal. O terceiro
uso dos genéricos é o tipo
do resultado da
operação, um número inteiro. Esse terceiro genérico deve ser igual
ao primeiro, o
que é lógico, pois o primeiro termo genérico
representa o subtotal. No caso de um fluxo sequencial, isso seria o fim dele. Mas para permitir o processamento
paralelo, será necessário que o operador binário combine dois resultados em um. Usando esse operador binário, combinamos os resultados
de dois segmentos. Isso é
repetido pelo stream até que todos os resultados dos threads sejam
mesclados novamente em um. Aqui eles são
preenchidos na operação de redução. Para reiterar,
começamos com uma folha limpa vazia como identidade
zero. No nosso caso. Em seguida, adicionamos o tamanho
da lista de dispositivos de todos os elementos do
fluxo a essa identidade. Se o fluxo for executado em paralelo, usamos para combiná-los
para adicionar dois subtotais em um até que tenhamos
um único resultado restante. Em nosso exemplo, reduzimos nossos objetos complexos
em um único número inteiro. Mas reduzido também pode
ser usado para reduzir todos os elementos de um fluxo em
um único objeto complexo. Por exemplo, poderíamos reduzir nossos clientes a um único
cliente contendo nossas informações. Mas vou deixar esse
como parte
dos exercícios que você
pode fazer sozinho.
13. Resumo e palavra em seguida: Chegamos ao
final desta aula. Vamos resumir para ver
o que aprendemos em nossa jornada na API
Java de oito fluxos. Começamos
aprendendo para que os streams são usados e como eles funcionam. Em seguida, detalhamos como você pode criar uma
expressão lambda a partir de uma classe interna anônima graças
às interfaces funcionais. Discutimos brevemente, mas as interfaces
funcionais são e quais você
costuma usar ao trabalhar com streams. Finalmente, analisamos as operações intermediárias
e terminais
comumente usadas. Obrigado por
assistir a esta aula. Espero que você tenha aprendido o suficiente
sobre streams para reconhecer situações nas quais você pode
e é capaz de usá-los. Se você quiser me ajudar a
melhorar esta aula e nos
dizer se
você a achou útil ou não. Eu ficaria muito grato se você
deixasse um comentário. Como prometido. Eu preparei alguns
exercícios para você fazer para ver se você pode aplicar o conhecimento aprendido
durante este curso. Cada exercício tem uma solicitação, um resultado esperado
para você verificar se o fez corretamente. Se houver algo
incerto sobre exercícios já
fornecidos pela aula, sinta-se à vontade para me enviar uma
mensagem com suas perguntas. Eu vou te ajudar
da melhor maneira possível. Mais uma vez. Obrigado por assistir
e até a próxima vez.