Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Aprenda API de Transmissão | enum e Stream API
Estruturas de Dados em Java

bookAPI de Transmissão

Existem várias maneiras de processar dados em Java - loops, métodos e diferentes algoritmos. No entanto, no Java 8, foi introduzida uma ferramenta muito poderosa - a API Stream.

Em termos simples, a API de Stream é uma maneira de trabalhar de forma rápida e fácil com um fluxo de informações. No nosso caso, esse fluxo de informações é representado por coleções. A API de Stream possui alguns conceitos. Aqui estão os principais.

Conceitos Principais

  • Stream: Representa uma sequência de elementos de dados que podem ser processados.

  • Operações Intermediárias: Operações que criam um novo stream após a sua execução. Exemplos: filter, map, distinct, sorted.

  • Operações Terminais: Operações que completam o processamento do stream e retornam um resultado. Exemplos: collect, forEach, count, reduce.

  • Streams Paralelos: Permitem o processamento paralelo de dados. Os métodos parallel() e parallelStream() são utilizados para criar streams paralelos.

Chega de falar sobre teoria, vamos começar a programar!

A declaração de um stream é feita utilizando um método na coleção que desejamos transformar em stream:

main.java

main.java

copy
12
List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream();

Com o método stream(), obtivemos um fluxo de strings. Porém, para começar a trabalhar com o fluxo, precisamos entender o que são expressões lambda, pois os métodos de fluxo trabalham principalmente com elas.

Expressões Lambda

Expressões lambda foram introduzidas no Java 8 e representam uma forma simplificada de criar funções anônimas em Java. Ainda não abordamos funções anônimas anteriormente, pois não eram extremamente necessárias, mas agora vamos nos familiarizar com elas por meio das expressões lambda.

Sintaxe da Expressão Lambda:

A sintaxe geral para expressões lambda em Java é a seguinte:

example.java

example.java

copy
123
(parameters) -> expression // or (parameters) -> { statements; }
  • Parâmetros: Esta é uma lista de parâmetros que pode estar vazia ou conter um ou mais parâmetros.

  • Seta: Representada pelo símbolo ->, que separa os parâmetros do corpo da expressão lambda.

  • Expressão ou Declarações: Este é o corpo da função, contendo uma expressão ou um bloco de instruções.

Aqui está um exemplo de uma expressão lambda representando uma função simples que soma dois números:

example.java

example.java

copy
12345678910
// Traditional way MathOperation addition = new MathOperation() { @Override public int operate(int a, int b) { return a + b; } }; // Using a lambda expression MathOperation addition = (int a, int b) -> a + b;

Vamos examinar mais de perto o que está acontecendo no código acima e como usamos expressões lambda:

main.java

main.java

copy
1234567891011121314
package com.example; // Functional interface with a single abstract method interface MyMathOperation { int operate(int a, int b); } public class Main { public static void main(String[] args) { // Using a lambda expression to implement the interface MyMathOperation addition = (a, b) -> a + b; System.out.println("Sum: " + addition.operate(5, 3)); } }

Explicação:

  • Criada uma interface funcional MyMathOperation com um único método abstrato operate.
  • Utilizada uma expressão lambda para implementar este método, realizando a soma de dois números.
  • Impresso o resultado da adição.

Entendo que possa ser desafiador compreender o que está acontecendo neste código por enquanto, mas vamos voltar para a API Stream, onde expressões lambda são frequentemente utilizadas, e tentar entender como usá-las na prática.

Como você deve se lembrar, anteriormente, nós criamos um fluxo de strings a partir de uma lista de strings. Agora, vamos utilizar métodos de stream para transformar cada string deste fluxo em maiúsculas:

main.java

main.java

copy
12345678910111213
package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream(); stream.map(e -> e.toUpperCase()).toList(); } }

No código acima, utilizamos uma expressão lambda e dois métodos: map() e toList(). Se está claro o que o método toList() faz, o método map() altera cada elemento no fluxo de acordo com a expressão lambda fornecida.

Vamos examinar mais de perto como a expressão lambda funciona aqui:

O método map() aplica o método toUpperCase() em cada elemento do fluxo. Nós definimos o elemento deste fluxo como e e, utilizando a expressão lambda, instruímos o programa a aplicar este método a cada elemento.

Mas isso não é o fim ainda, pois aplicamos uma operação intermediária. Isso significa que as operações no fluxo ainda não foram concluídas. Para completar o trabalho no fluxo, precisamos aplicar uma operação terminal, que finalizará as operações no fluxo e retornará um valor específico. Por exemplo, podemos usar o método toList(), e o fluxo modificado será convertido em uma lista.

Por exemplo:

main.java

main.java

copy
1234567891011121314
package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream(); List<String> list = stream.map(e -> e.toUpperCase()).toList(); System.out.println(list); } }

Nota

Observe que após usar a operação terminal, não é mais possível utilizar métodos de stream. No nosso caso, após a operação terminal toList(), nosso stream foi convertido em uma lista, logo não podemos usar métodos de stream na lista.

Vamos examinar mais de perto as possíveis operações intermediárias no stream.

Operações intermediárias

  • O método map() - você já está familiarizado com este método; ele realiza operações especificadas pela expressão lambda em cada elemento do fluxo.

Por exemplo, vamos utilizar o método substring() em cada elemento no fluxo de strings:

main.java

main.java

copy
123456789101112131415
package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); Stream<String> stream = strings.stream(); List<String> list = stream.map(e -> e.substring(1, 4)).toList(); System.out.println("Modified list: " + list); } }
  • O método filter() recebe uma expressão lambda com uma condição com base na qual o fluxo será filtrado. Em outras palavras, todos os elementos que atendem à condição irão permanecer no fluxo, e elementos que não atendem à condição serão removidos do fluxo. Vamos modificar o fluxo para manter apenas os elementos cujo comprimento é maior que 5:
main.java

main.java

copy
12345678910111213141516
package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); Stream<String> stream = strings.stream(); stream = stream.filter(e -> e.length() > 5); List<String> list = stream.map(e -> e.substring(1, 4)).toList(); System.out.println("Modified list: " + list); } }

Usando o método filter(), removemos a string "with" do fluxo porque esta palavra tem menos de 5 caracteres.

Você também pode usar operações intermediárias várias vezes seguidas.

Por exemplo, podemos simplificar um pouco o código acima:

main.java

main.java

copy
12345678910111213141516
package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); List<String> list = strings.stream() .filter(e -> e.length() > 5) .map(e -> e.substring(1, 4)) .toList(); System.out.println("Modified list: " + list); } }

Ao encadear múltiplos métodos de stream juntos, é recomendável colocar cada método em uma nova linha para melhorar significativamente a legibilidade do código.

  • O método flatMap() transforma cada elemento de um fluxo em um novo fluxo e combina os resultados em um único fluxo. Em outras palavras, com esse método, podemos dividir o fluxo em fluxos, e então eles serão mesclados em um único fluxo. Por exemplo, temos uma lista de strings onde cada string pode conter mais de uma palavra, como uma lista de nomes e sobrenomes. E precisamos capitalizar a primeira letra de cada uma dessas palavras:
main.java

main.java

copy
123456789101112131415161718192021222324
package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<String> users = Arrays.asList("Ethan Johnson", "Olivia smith", "mason davis", "Ava taylor", "logan brown", "Emma Anderson", "jackson miller"); System.out.println("List of users: " + users); List<String> list = users.stream() .flatMap(e -> Arrays.stream(e.split(" "))) .map(e -> capitalizeFirstLetter(e)) .toList(); System.out.println("List with capitalized names and surnames: " + list); } private static String capitalizeFirstLetter(String word) { if (word == null || word.isEmpty()) { return word; } return Character.toUpperCase(word.charAt(0)) + word.substring(1); } }

No código acima, escrevemos um método privado separado que capitaliza a primeira letra de uma palavra e utilizamos este método no método map() juntamente com uma expressão lambda.

Note que ao usar o método flatMap, dividimos cada elemento do fluxo em diferentes fluxos usando o método Arrays.stream(e.split(" ")). Como o método split() retorna um array, precisamos usar o método Arrays.stream() para dividir este array em fluxos.

Depois, todos esses fluxos são fundidos em um único fluxo, após o qual usamos o método que escrevemos. E agora temos todos os nomes e sobrenomes dos usuários com a primeira letra maiúscula.

Sabe o que seria legal? Se colocássemos esses nomes e sobrenomes em um HashMap, onde a chave é o sobrenome e o valor é o nome.

Vamos implementar isso no código:

main.java

main.java

copy
12345678910111213141516171819202122232425262728293031323334
package com.example; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; public class Main { public static void main(String[] args) { List<String> users = Arrays.asList("Ethan Johnson", "Olivia smith", "mason davis", "Ava taylor", "logan brown", "Emma Anderson", "jackson miller"); System.out.println("List of users: " + users); List<String> list = users.stream() .flatMap(e -> Arrays.stream(e.split(" "))) .map(e -> capitalizeFirstLetter(e)) .toList(); System.out.println("List with capitalized names and surnames: " + list); Map<String, String> usersKeyValue = new HashMap<>(); for (int i = 0; i < list.size() - 1; i+=2) { String name = list.get(i); String surname = list.get(i + 1); usersKeyValue.put(surname, name); } System.out.println("Map with surnames as keys and names as values: " + usersKeyValue); } private static String capitalizeFirstLetter(String word) { if (word == null || word.isEmpty()) { return word; } return Character.toUpperCase(word.charAt(0)) + word.substring(1); } }

Com um loop simples, armazenamos o nome e o sobrenome em variáveis e depois no mapa. Observe como o loop funciona. Incrementamos a variável i em 2 a cada iteração porque precisamos pular o sobrenome depois de já tê-lo registrado.

Nota

Este método de filtragem de dados é bastante arriscado porque os dados podem ser registrados na ordem errada, mas no nosso caso, isso não tem um papel significativo.

  • O método distinct() remove duplicatas do fluxo. Em geral, isso pode ser útil se você precisar de elementos únicos no fluxo ou se quiser eliminar rapidamente duplicatas de uma lista. Você pode facilmente conseguir isso com a seguinte construção:
list.stream().distinct().toList()
  • O método sorted ordena todos os elementos do fluxo em ordem natural, do menor para o maior número ou em ordem alfabética. Isso também pode ser útil se você precisar de um fluxo ordenado ou se precisar ordenar rapidamente uma lista.

  • O método skip(n) ignora os primeiros n elementos do fluxo. Isso pode ser útil ao trabalhar com arquivos de texto, onde as primeiras n linhas podem ser, por exemplo, metadados ou uma descrição do arquivo. Vale também mencionar o método limit(n), que geralmente limita o número de elementos no fluxo. Mesmo que criemos um fluxo com 1000 elementos e depois usemos limit(200), o fluxo conterá apenas os primeiros 200 elementos.

main.java

main.java

copy
123456789101112
package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); example = example.stream().skip(3).limit(5).toList(); System.out.println("List: " + example); } }

Estes são os principais métodos intermediários que você precisará usar. Você pode explorar o restante dos métodos consultando a link para a documentação oficial do Java. Vamos prosseguir com os métodos terminais.

Métodos Terminais

  • O método terminal com o qual você já está familiarizado é toList(). Ele converte o fluxo em uma lista e a retorna. Em outras palavras, podemos atribuir diretamente este fluxo com métodos a uma lista. Este método foi introduzido no Java 17 e serve como substituto para a construção mais complexa collect(Collectors.toList()).

  • O método collect() também converte o fluxo em uma estrutura de dados específica. Ele utiliza, como parâmetro, um método da interface Collectors. Esta interface possui métodos como toList(), toSet() e toCollection(). Por exemplo:

main.java

main.java

copy
123456789101112131415
package com.example; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); Set<Integer> integerSet = example.stream().collect(Collectors.toSet()); System.out.println("List: " + example); System.out.println("Set: " + integerSet); } }

O método forEach() recebe uma expressão lambda e realiza uma ação específica para cada elemento no stream.

Por exemplo:

main.java

main.java

copy
1234567891011
package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); example.stream().forEach(e -> System.out.println(e + 1)); } }

A diferença entre este método e o método map é que este método é terminal, e após utilizá-lo, não é possível chamar outros métodos.

Estes são todos os métodos básicos para trabalhar com streams. É um tópico complexo e talvez você não o compreenda imediatamente. Contudo, é um assunto que se domina com a prática. Nos próximos capítulos práticos sobre streams, você terá várias oportunidades para trabalhar com eles, já que é uma forma muito conveniente e prática de manipular listas e arrays de dados!

1. Qual é o propósito principal da API Stream em Java?

2. Qual das seguintes é uma operação terminal na API de Stream?

3. O que a operação map faz na API Stream?

4. Qual é a diferença entre a operação flatMap e map na API Stream?

5. O que a operação filter faz na API Stream?

6. Qual é o propósito da operação forEach na API de Stream?

7. Qual das seguintes é uma operação intermediária na API de Stream?

8. Como é usada a operação limit na API Stream?

question mark

Qual é o propósito principal da API Stream em Java?

Select the correct answer

question mark

Qual das seguintes é uma operação terminal na API de Stream?

Select the correct answer

question mark

O que a operação map faz na API Stream?

Select the correct answer

question mark

Qual é a diferença entre a operação flatMap e map na API Stream?

Select the correct answer

question mark

O que a operação filter faz na API Stream?

Select the correct answer

question mark

Qual é o propósito da operação forEach na API de Stream?

Select the correct answer

question mark

Qual das seguintes é uma operação intermediária na API de Stream?

Select the correct answer

question mark

Como é usada a operação limit na API Stream?

Select the correct answer

Tudo estava claro?

Como podemos melhorá-lo?

Obrigado pelo seu feedback!

Seção 4. Capítulo 3

Pergunte à IA

expand

Pergunte à IA

ChatGPT

Pergunte o que quiser ou experimente uma das perguntas sugeridas para iniciar nosso bate-papo

Suggested prompts:

What are some examples of using the Stream API in Java?

Can you explain more about lambda expressions and how they work?

How do intermediate and terminal operations differ in practice?

Awesome!

Completion rate improved to 4

bookAPI de Transmissão

Deslize para mostrar o menu

Existem várias maneiras de processar dados em Java - loops, métodos e diferentes algoritmos. No entanto, no Java 8, foi introduzida uma ferramenta muito poderosa - a API Stream.

Em termos simples, a API de Stream é uma maneira de trabalhar de forma rápida e fácil com um fluxo de informações. No nosso caso, esse fluxo de informações é representado por coleções. A API de Stream possui alguns conceitos. Aqui estão os principais.

Conceitos Principais

  • Stream: Representa uma sequência de elementos de dados que podem ser processados.

  • Operações Intermediárias: Operações que criam um novo stream após a sua execução. Exemplos: filter, map, distinct, sorted.

  • Operações Terminais: Operações que completam o processamento do stream e retornam um resultado. Exemplos: collect, forEach, count, reduce.

  • Streams Paralelos: Permitem o processamento paralelo de dados. Os métodos parallel() e parallelStream() são utilizados para criar streams paralelos.

Chega de falar sobre teoria, vamos começar a programar!

A declaração de um stream é feita utilizando um método na coleção que desejamos transformar em stream:

main.java

main.java

copy
12
List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream();

Com o método stream(), obtivemos um fluxo de strings. Porém, para começar a trabalhar com o fluxo, precisamos entender o que são expressões lambda, pois os métodos de fluxo trabalham principalmente com elas.

Expressões Lambda

Expressões lambda foram introduzidas no Java 8 e representam uma forma simplificada de criar funções anônimas em Java. Ainda não abordamos funções anônimas anteriormente, pois não eram extremamente necessárias, mas agora vamos nos familiarizar com elas por meio das expressões lambda.

Sintaxe da Expressão Lambda:

A sintaxe geral para expressões lambda em Java é a seguinte:

example.java

example.java

copy
123
(parameters) -> expression // or (parameters) -> { statements; }
  • Parâmetros: Esta é uma lista de parâmetros que pode estar vazia ou conter um ou mais parâmetros.

  • Seta: Representada pelo símbolo ->, que separa os parâmetros do corpo da expressão lambda.

  • Expressão ou Declarações: Este é o corpo da função, contendo uma expressão ou um bloco de instruções.

Aqui está um exemplo de uma expressão lambda representando uma função simples que soma dois números:

example.java

example.java

copy
12345678910
// Traditional way MathOperation addition = new MathOperation() { @Override public int operate(int a, int b) { return a + b; } }; // Using a lambda expression MathOperation addition = (int a, int b) -> a + b;

Vamos examinar mais de perto o que está acontecendo no código acima e como usamos expressões lambda:

main.java

main.java

copy
1234567891011121314
package com.example; // Functional interface with a single abstract method interface MyMathOperation { int operate(int a, int b); } public class Main { public static void main(String[] args) { // Using a lambda expression to implement the interface MyMathOperation addition = (a, b) -> a + b; System.out.println("Sum: " + addition.operate(5, 3)); } }

Explicação:

  • Criada uma interface funcional MyMathOperation com um único método abstrato operate.
  • Utilizada uma expressão lambda para implementar este método, realizando a soma de dois números.
  • Impresso o resultado da adição.

Entendo que possa ser desafiador compreender o que está acontecendo neste código por enquanto, mas vamos voltar para a API Stream, onde expressões lambda são frequentemente utilizadas, e tentar entender como usá-las na prática.

Como você deve se lembrar, anteriormente, nós criamos um fluxo de strings a partir de uma lista de strings. Agora, vamos utilizar métodos de stream para transformar cada string deste fluxo em maiúsculas:

main.java

main.java

copy
12345678910111213
package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream(); stream.map(e -> e.toUpperCase()).toList(); } }

No código acima, utilizamos uma expressão lambda e dois métodos: map() e toList(). Se está claro o que o método toList() faz, o método map() altera cada elemento no fluxo de acordo com a expressão lambda fornecida.

Vamos examinar mais de perto como a expressão lambda funciona aqui:

O método map() aplica o método toUpperCase() em cada elemento do fluxo. Nós definimos o elemento deste fluxo como e e, utilizando a expressão lambda, instruímos o programa a aplicar este método a cada elemento.

Mas isso não é o fim ainda, pois aplicamos uma operação intermediária. Isso significa que as operações no fluxo ainda não foram concluídas. Para completar o trabalho no fluxo, precisamos aplicar uma operação terminal, que finalizará as operações no fluxo e retornará um valor específico. Por exemplo, podemos usar o método toList(), e o fluxo modificado será convertido em uma lista.

Por exemplo:

main.java

main.java

copy
1234567891011121314
package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream(); List<String> list = stream.map(e -> e.toUpperCase()).toList(); System.out.println(list); } }

Nota

Observe que após usar a operação terminal, não é mais possível utilizar métodos de stream. No nosso caso, após a operação terminal toList(), nosso stream foi convertido em uma lista, logo não podemos usar métodos de stream na lista.

Vamos examinar mais de perto as possíveis operações intermediárias no stream.

Operações intermediárias

  • O método map() - você já está familiarizado com este método; ele realiza operações especificadas pela expressão lambda em cada elemento do fluxo.

Por exemplo, vamos utilizar o método substring() em cada elemento no fluxo de strings:

main.java

main.java

copy
123456789101112131415
package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); Stream<String> stream = strings.stream(); List<String> list = stream.map(e -> e.substring(1, 4)).toList(); System.out.println("Modified list: " + list); } }
  • O método filter() recebe uma expressão lambda com uma condição com base na qual o fluxo será filtrado. Em outras palavras, todos os elementos que atendem à condição irão permanecer no fluxo, e elementos que não atendem à condição serão removidos do fluxo. Vamos modificar o fluxo para manter apenas os elementos cujo comprimento é maior que 5:
main.java

main.java

copy
12345678910111213141516
package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); Stream<String> stream = strings.stream(); stream = stream.filter(e -> e.length() > 5); List<String> list = stream.map(e -> e.substring(1, 4)).toList(); System.out.println("Modified list: " + list); } }

Usando o método filter(), removemos a string "with" do fluxo porque esta palavra tem menos de 5 caracteres.

Você também pode usar operações intermediárias várias vezes seguidas.

Por exemplo, podemos simplificar um pouco o código acima:

main.java

main.java

copy
12345678910111213141516
package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); List<String> list = strings.stream() .filter(e -> e.length() > 5) .map(e -> e.substring(1, 4)) .toList(); System.out.println("Modified list: " + list); } }

Ao encadear múltiplos métodos de stream juntos, é recomendável colocar cada método em uma nova linha para melhorar significativamente a legibilidade do código.

  • O método flatMap() transforma cada elemento de um fluxo em um novo fluxo e combina os resultados em um único fluxo. Em outras palavras, com esse método, podemos dividir o fluxo em fluxos, e então eles serão mesclados em um único fluxo. Por exemplo, temos uma lista de strings onde cada string pode conter mais de uma palavra, como uma lista de nomes e sobrenomes. E precisamos capitalizar a primeira letra de cada uma dessas palavras:
main.java

main.java

copy
123456789101112131415161718192021222324
package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<String> users = Arrays.asList("Ethan Johnson", "Olivia smith", "mason davis", "Ava taylor", "logan brown", "Emma Anderson", "jackson miller"); System.out.println("List of users: " + users); List<String> list = users.stream() .flatMap(e -> Arrays.stream(e.split(" "))) .map(e -> capitalizeFirstLetter(e)) .toList(); System.out.println("List with capitalized names and surnames: " + list); } private static String capitalizeFirstLetter(String word) { if (word == null || word.isEmpty()) { return word; } return Character.toUpperCase(word.charAt(0)) + word.substring(1); } }

No código acima, escrevemos um método privado separado que capitaliza a primeira letra de uma palavra e utilizamos este método no método map() juntamente com uma expressão lambda.

Note que ao usar o método flatMap, dividimos cada elemento do fluxo em diferentes fluxos usando o método Arrays.stream(e.split(" ")). Como o método split() retorna um array, precisamos usar o método Arrays.stream() para dividir este array em fluxos.

Depois, todos esses fluxos são fundidos em um único fluxo, após o qual usamos o método que escrevemos. E agora temos todos os nomes e sobrenomes dos usuários com a primeira letra maiúscula.

Sabe o que seria legal? Se colocássemos esses nomes e sobrenomes em um HashMap, onde a chave é o sobrenome e o valor é o nome.

Vamos implementar isso no código:

main.java

main.java

copy
12345678910111213141516171819202122232425262728293031323334
package com.example; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; public class Main { public static void main(String[] args) { List<String> users = Arrays.asList("Ethan Johnson", "Olivia smith", "mason davis", "Ava taylor", "logan brown", "Emma Anderson", "jackson miller"); System.out.println("List of users: " + users); List<String> list = users.stream() .flatMap(e -> Arrays.stream(e.split(" "))) .map(e -> capitalizeFirstLetter(e)) .toList(); System.out.println("List with capitalized names and surnames: " + list); Map<String, String> usersKeyValue = new HashMap<>(); for (int i = 0; i < list.size() - 1; i+=2) { String name = list.get(i); String surname = list.get(i + 1); usersKeyValue.put(surname, name); } System.out.println("Map with surnames as keys and names as values: " + usersKeyValue); } private static String capitalizeFirstLetter(String word) { if (word == null || word.isEmpty()) { return word; } return Character.toUpperCase(word.charAt(0)) + word.substring(1); } }

Com um loop simples, armazenamos o nome e o sobrenome em variáveis e depois no mapa. Observe como o loop funciona. Incrementamos a variável i em 2 a cada iteração porque precisamos pular o sobrenome depois de já tê-lo registrado.

Nota

Este método de filtragem de dados é bastante arriscado porque os dados podem ser registrados na ordem errada, mas no nosso caso, isso não tem um papel significativo.

  • O método distinct() remove duplicatas do fluxo. Em geral, isso pode ser útil se você precisar de elementos únicos no fluxo ou se quiser eliminar rapidamente duplicatas de uma lista. Você pode facilmente conseguir isso com a seguinte construção:
list.stream().distinct().toList()
  • O método sorted ordena todos os elementos do fluxo em ordem natural, do menor para o maior número ou em ordem alfabética. Isso também pode ser útil se você precisar de um fluxo ordenado ou se precisar ordenar rapidamente uma lista.

  • O método skip(n) ignora os primeiros n elementos do fluxo. Isso pode ser útil ao trabalhar com arquivos de texto, onde as primeiras n linhas podem ser, por exemplo, metadados ou uma descrição do arquivo. Vale também mencionar o método limit(n), que geralmente limita o número de elementos no fluxo. Mesmo que criemos um fluxo com 1000 elementos e depois usemos limit(200), o fluxo conterá apenas os primeiros 200 elementos.

main.java

main.java

copy
123456789101112
package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); example = example.stream().skip(3).limit(5).toList(); System.out.println("List: " + example); } }

Estes são os principais métodos intermediários que você precisará usar. Você pode explorar o restante dos métodos consultando a link para a documentação oficial do Java. Vamos prosseguir com os métodos terminais.

Métodos Terminais

  • O método terminal com o qual você já está familiarizado é toList(). Ele converte o fluxo em uma lista e a retorna. Em outras palavras, podemos atribuir diretamente este fluxo com métodos a uma lista. Este método foi introduzido no Java 17 e serve como substituto para a construção mais complexa collect(Collectors.toList()).

  • O método collect() também converte o fluxo em uma estrutura de dados específica. Ele utiliza, como parâmetro, um método da interface Collectors. Esta interface possui métodos como toList(), toSet() e toCollection(). Por exemplo:

main.java

main.java

copy
123456789101112131415
package com.example; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); Set<Integer> integerSet = example.stream().collect(Collectors.toSet()); System.out.println("List: " + example); System.out.println("Set: " + integerSet); } }

O método forEach() recebe uma expressão lambda e realiza uma ação específica para cada elemento no stream.

Por exemplo:

main.java

main.java

copy
1234567891011
package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); example.stream().forEach(e -> System.out.println(e + 1)); } }

A diferença entre este método e o método map é que este método é terminal, e após utilizá-lo, não é possível chamar outros métodos.

Estes são todos os métodos básicos para trabalhar com streams. É um tópico complexo e talvez você não o compreenda imediatamente. Contudo, é um assunto que se domina com a prática. Nos próximos capítulos práticos sobre streams, você terá várias oportunidades para trabalhar com eles, já que é uma forma muito conveniente e prática de manipular listas e arrays de dados!

1. Qual é o propósito principal da API Stream em Java?

2. Qual das seguintes é uma operação terminal na API de Stream?

3. O que a operação map faz na API Stream?

4. Qual é a diferença entre a operação flatMap e map na API Stream?

5. O que a operação filter faz na API Stream?

6. Qual é o propósito da operação forEach na API de Stream?

7. Qual das seguintes é uma operação intermediária na API de Stream?

8. Como é usada a operação limit na API Stream?

question mark

Qual é o propósito principal da API Stream em Java?

Select the correct answer

question mark

Qual das seguintes é uma operação terminal na API de Stream?

Select the correct answer

question mark

O que a operação map faz na API Stream?

Select the correct answer

question mark

Qual é a diferença entre a operação flatMap e map na API Stream?

Select the correct answer

question mark

O que a operação filter faz na API Stream?

Select the correct answer

question mark

Qual é o propósito da operação forEach na API de Stream?

Select the correct answer

question mark

Qual das seguintes é uma operação intermediária na API de Stream?

Select the correct answer

question mark

Como é usada a operação limit na API Stream?

Select the correct answer

Tudo estava claro?

Como podemos melhorá-lo?

Obrigado pelo seu feedback!

Seção 4. Capítulo 3
some-alt