Java 8 Streams, uma introdução prática | Jeronemo | Skillshare

Velocidade de reprodução


1.0x


  • 0.5x
  • 0.75x
  • 1x (Normal)
  • 1.25x
  • 1.5x
  • 1.75x
  • 2x

Java 8 Streams, uma introdução prática

teacher avatar Jeronemo

Assista a este curso e milhares de outros

Tenha acesso ilimitado a todos os cursos
Oferecidos por líderes do setor e profissionais do mercado
Os temas incluem ilustração, design, fotografia e muito mais

Assista a este curso e milhares de outros

Tenha acesso ilimitado a todos os cursos
Oferecidos por líderes do setor e profissionais do mercado
Os temas incluem ilustração, design, fotografia e muito mais

Aulas neste curso

    • 1.

      Apresentação

      0:41

    • 2.

      Uso de fluxo e caso de uso

      3:28

    • 3.

      Interfaces funcionais e Lambda

      10:48

    • 4.

      Operações de fluxo

      1:52

    • 5.

      Operação intermediária: filtro

      1:19

    • 6.

      Operação de terminais: forEach

      1:31

    • 7.

      Operação intermediária: mapa

      1:34

    • 8.

      Operação intermediária: flatMap

      2:36

    • 9.

      Operações intermediárias: sequencial e paralelo

      2:48

    • 10.

      Operação intermediária:

      1:33

    • 11.

      Operação de terminais: coletar

      4:06

    • 12.

      Operação de terminais: reduza

      5:11

    • 13.

      Sumário e palavra seguinte

      1:31

  • --
  • Nível iniciante
  • Nível intermediário
  • Nível avançado
  • Todos os níveis

Gerado pela comunidade

O nível é determinado pela opinião da maioria dos estudantes que avaliaram este curso. Mostramos a recomendação do professor até que sejam coletadas as respostas de pelo menos 5 estudantes.

34

Estudantes

--

Projeto

Sobre este curso

Os fluxos foram introduzidos no Java 8, mas são tão relevantes no Java 11, Java 17 e todas as versões para vir. Eles são um aspecto fundamental para se tornar um desenvolvedor Java melhor.

Este curso vai ensinar tudo o que você precisa saber sobre o Java Streams para começar a usá-los em seus próprios projetos. Seu objetivo é dar uma visão sobre as possibilidades do Streams, além de permitir que você reconheça situações em que o Streams pode ser útil. Ele se concentra em pessoas que têm pouca ou nenhuma experiência com o Streams & lambdas, mas também pode ser usado como curso de atualização nas operações mais usadas e sua utilização.

O que você vai aprender neste curso:

  • Como inter-classes anônimas, interfaces funcionais e lambdas funcionam e são criadas.
  • Como funciona os fluxos em geral e por que você deve usá-los.
  • Como as operações mais usadas em um Stream funcionam.

Streams são um passo para a programação funcional. Masterização de Streams torna seu código legível como uma história, o que por sua vez torna ainda mais sustentável por você e por outros. No final há exercícios para colocar tudo em prática.

A quem se destina este curso:

Qualquer desenvolvedor Java interessado em Streams ou programação funcional em geral. Embora o título mencione o Java 8 (que era sua versão de introdução), é uma ótima habilidade ter em qual versão do Java você trabalha. Espera-se que você tenha uma compreensão básica de usar Java, como compilar e executar código, além de estender aulas e implementar interfaces.

Materiais necessários para este curso:

Nenhum material é necessário para seguir com este curso. No entanto, se você quiser fazer os exercícios no final, você precisará ter JDK 8 ou superior instalado. Uma maneira de editar e executar os arquivos .java também é necessária. Isso pode ser tão simples como o bloco de notas, mas eu recomendo usar um IDE como o IntelliJ ou o
Eclipse.

Conheça seu professor

Teacher Profile Image

Jeronemo

Professor

Hey there, welcome to my profile! I have the classical Dutch name Jeroen, but you may call me Jeronemo.

I've been a backend developer for the good part of a decade, dabbing mostly in microservice architecture using REST, Java & Spring Boot, but having worked with loads of other stuff like ADO, Docker, ELK stack, Eureka, Flyway, Gitlab, GWT, Hazelcast, Hibernate, Hoverfly, IntelliJ, Jackson, Jenkins, JIRA, JSON, Junit, Kubernetes, RabbitMQ, Maven, Mockito, Nexus, SOAP, Sonar, SQL, WDSL, XSD & Zuul.

Visualizar o perfil completo

Level: All Levels

Nota do curso

As expectativas foram atingidas?
    Superou!
  • 0%
  • Sim
  • 0%
  • Um pouco
  • 0%
  • Não
  • 0%

Por que fazer parte da Skillshare?

Faça cursos premiados Skillshare Original

Cada curso possui aulas curtas e projetos práticos

Sua assinatura apoia os professores da Skillshare

Aprenda em qualquer lugar

Faça cursos em qualquer lugar com o aplicativo da Skillshare. Assista no avião, no metrô ou em qualquer lugar que funcione melhor para você, por streaming ou download.

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.