Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Aprenda API de Stream em Java | Recursos e Técnicas Avançadas de Java
Estruturas de Dados em Java

bookAPI de Stream em Java

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

Em termos simples, a Stream API é uma forma de trabalhar de maneira rápida e fácil com um fluxo de informações. Neste caso, esse fluxo de informações é representado por coleções. A Stream API 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 sua execução. Exemplos: filter, map, distinct, sorted;

  • Operações terminais: operações que finalizam o processamento do stream e retornam um resultado. Exemplos: collect, forEach, count, reduce;

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

Chega de teoria, vamos começar a programar!

A declaração de um stream é feita utilizando um método na coleção que se deseja 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(), foi obtido um stream de strings. No entanto, para começar a trabalhar com o stream, é necessário compreender o que são expressões lambda, pois os métodos de stream funcionam 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. Funções anônimas ainda não foram abordadas, pois não eram altamente necessárias, mas agora serão apresentadas 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 instruçõ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 que representa 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 analisar mais de perto o que exatamente está acontecendo no código acima e como utilizamos 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)); } }

No código acima:

Foi criada uma interface funcional MyMathOperation com um único método abstrato operate.

Utilizou-se uma expressão lambda para implementar este método, realizando a soma de dois números.

O resultado da soma foi impresso.

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

Como você deve se lembrar, anteriormente criamos um stream de strings a partir de uma lista de strings. Agora, vamos utilizar métodos de stream para tornar cada string deste stream maiúscula:

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, foi utilizada 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 do stream de acordo com a expressão lambda fornecida.

Vamos analisar mais detalhadamente como a expressão lambda funciona neste caso:

O método map() aplica o método toUpperCase() a cada elemento do stream. Definimos o elemento deste stream como e e, utilizando a expressão lambda, instruímos o programa a aplicar este método a cada elemento.

No entanto, isso ainda não é o fim, pois foi aplicada uma operação intermediária. Isso significa que as operações no stream ainda não foram concluídas. Para finalizar o processamento do stream, é necessário aplicar uma operação terminal, que encerrará as operações no stream e retornará um valor específico. Por exemplo, pode-se utilizar o método toList(), e o stream 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); } }

Vamos analisar mais de perto as possíveis operações intermediárias em streams.

Operações Intermediárias

O método map() — já conhecido, realiza operações especificadas pela expressão lambda em cada elemento do stream.

Por exemplo, utilize o método substring() em cada elemento de um stream 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 stream será filtrado. Ou seja, todos os elementos que atendem à condição permanecem no stream, e os elementos que não atendem à condição são removidos do stream. Vamos modificar o stream para manter apenas os elementos cujo comprimento seja 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); } }

Utilizando o método filter(), removemos a string "with" do stream porque esta palavra possui menos de 5 caracteres.

Também é possível utilizar operações intermediárias múltiplas em sequência.

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étodos de stream múltiplos, recomenda-se 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 stream em um novo stream e combina os resultados em um único stream. Em outras palavras, com este método, podemos dividir o stream em vários streams, e então eles serão mesclados em um único stream. Por exemplo, você tem uma lista de strings onde cada string pode conter mais de uma palavra, como uma lista de nomes e sobrenomes. E você precisa colocar a primeira letra de cada uma dessas palavras em maiúsculo:

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, foi criado um método privado separado que coloca a primeira letra de uma palavra em maiúsculo e utilizou-se esse método no map() junto com uma expressão lambda.

Observe que, ao usar o método flatMap, cada elemento do stream é dividido em diferentes streams utilizando o método Arrays.stream(e.split(" ")). Como o método split() retorna um array, é necessário usar o método Arrays.stream() para dividir esse array em streams.

Depois, todos esses streams são mesclados em um único stream, e então utilizamos o método criado anteriormente. Agora temos todos os nomes e sobrenomes dos usuários com a primeira letra em maiúsculo.

Sabe o que seria interessante? 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 laço simples, armazenamos o primeiro nome e o sobrenome em variáveis e depois no mapa. Observe como o laço funciona. Incrementamos a variável i de 2 em 2 a cada iteração porque precisamos pular o sobrenome depois de já tê-lo registrado.

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

  • O método skip(n) ignora os primeiros n elementos do stream. 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 mencionar também o método limit(n), que geralmente limita a quantidade de elementos no stream. Mesmo que criemos um stream com 1000 elementos e depois utilizemos limit(200), o stream 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á utilizar. É possível explorar os demais métodos consultando a link para a documentação oficial do Java. Vamos avançar para os métodos terminais.

Métodos Terminais

  • O método terminal com o qual você já está familiarizado é o toList(). Ele converte o stream em uma lista e a retorna. Ou seja, é possível atribuir diretamente esse stream com métodos a uma lista. Este método foi introduzido no Java 17 e substitui a construção mais complexa collect(Collectors.toList());

  • O método collect() também converte o stream 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 executa 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 ele, 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 você pode não compreendê-lo imediatamente. No entanto, é um assunto que se domina com a prática. Nos próximos capítulos práticos sobre streams, haverá muitas oportunidades para trabalhar com eles, pois é uma forma muito conveniente e prática de manipular listas e arrays de dados!

1. Qual é o principal objetivo da Stream API em Java?

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

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

4. Como a operação flatMap é diferente de map na Stream API?

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

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

7. Qual das alternativas a seguir é uma operação intermediária na Stream API?

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

question mark

Qual é o principal objetivo da Stream API em Java?

Select the correct answer

question mark

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

Select the correct answer

question mark

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

Select the correct answer

question mark

Como a operação flatMap é diferente de map na Stream API?

Select the correct answer

question mark

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

Select the correct answer

question mark

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

Select the correct answer

question mark

Qual das alternativas a seguir é uma operação intermediária na Stream API?

Select the correct answer

question mark

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

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

bookAPI de Stream em Java

Deslize para mostrar o menu

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

Em termos simples, a Stream API é uma forma de trabalhar de maneira rápida e fácil com um fluxo de informações. Neste caso, esse fluxo de informações é representado por coleções. A Stream API 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 sua execução. Exemplos: filter, map, distinct, sorted;

  • Operações terminais: operações que finalizam o processamento do stream e retornam um resultado. Exemplos: collect, forEach, count, reduce;

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

Chega de teoria, vamos começar a programar!

A declaração de um stream é feita utilizando um método na coleção que se deseja 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(), foi obtido um stream de strings. No entanto, para começar a trabalhar com o stream, é necessário compreender o que são expressões lambda, pois os métodos de stream funcionam 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. Funções anônimas ainda não foram abordadas, pois não eram altamente necessárias, mas agora serão apresentadas 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 instruçõ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 que representa 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 analisar mais de perto o que exatamente está acontecendo no código acima e como utilizamos 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)); } }

No código acima:

Foi criada uma interface funcional MyMathOperation com um único método abstrato operate.

Utilizou-se uma expressão lambda para implementar este método, realizando a soma de dois números.

O resultado da soma foi impresso.

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

Como você deve se lembrar, anteriormente criamos um stream de strings a partir de uma lista de strings. Agora, vamos utilizar métodos de stream para tornar cada string deste stream maiúscula:

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, foi utilizada 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 do stream de acordo com a expressão lambda fornecida.

Vamos analisar mais detalhadamente como a expressão lambda funciona neste caso:

O método map() aplica o método toUpperCase() a cada elemento do stream. Definimos o elemento deste stream como e e, utilizando a expressão lambda, instruímos o programa a aplicar este método a cada elemento.

No entanto, isso ainda não é o fim, pois foi aplicada uma operação intermediária. Isso significa que as operações no stream ainda não foram concluídas. Para finalizar o processamento do stream, é necessário aplicar uma operação terminal, que encerrará as operações no stream e retornará um valor específico. Por exemplo, pode-se utilizar o método toList(), e o stream 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); } }

Vamos analisar mais de perto as possíveis operações intermediárias em streams.

Operações Intermediárias

O método map() — já conhecido, realiza operações especificadas pela expressão lambda em cada elemento do stream.

Por exemplo, utilize o método substring() em cada elemento de um stream 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 stream será filtrado. Ou seja, todos os elementos que atendem à condição permanecem no stream, e os elementos que não atendem à condição são removidos do stream. Vamos modificar o stream para manter apenas os elementos cujo comprimento seja 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); } }

Utilizando o método filter(), removemos a string "with" do stream porque esta palavra possui menos de 5 caracteres.

Também é possível utilizar operações intermediárias múltiplas em sequência.

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étodos de stream múltiplos, recomenda-se 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 stream em um novo stream e combina os resultados em um único stream. Em outras palavras, com este método, podemos dividir o stream em vários streams, e então eles serão mesclados em um único stream. Por exemplo, você tem uma lista de strings onde cada string pode conter mais de uma palavra, como uma lista de nomes e sobrenomes. E você precisa colocar a primeira letra de cada uma dessas palavras em maiúsculo:

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, foi criado um método privado separado que coloca a primeira letra de uma palavra em maiúsculo e utilizou-se esse método no map() junto com uma expressão lambda.

Observe que, ao usar o método flatMap, cada elemento do stream é dividido em diferentes streams utilizando o método Arrays.stream(e.split(" ")). Como o método split() retorna um array, é necessário usar o método Arrays.stream() para dividir esse array em streams.

Depois, todos esses streams são mesclados em um único stream, e então utilizamos o método criado anteriormente. Agora temos todos os nomes e sobrenomes dos usuários com a primeira letra em maiúsculo.

Sabe o que seria interessante? 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 laço simples, armazenamos o primeiro nome e o sobrenome em variáveis e depois no mapa. Observe como o laço funciona. Incrementamos a variável i de 2 em 2 a cada iteração porque precisamos pular o sobrenome depois de já tê-lo registrado.

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

  • O método skip(n) ignora os primeiros n elementos do stream. 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 mencionar também o método limit(n), que geralmente limita a quantidade de elementos no stream. Mesmo que criemos um stream com 1000 elementos e depois utilizemos limit(200), o stream 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á utilizar. É possível explorar os demais métodos consultando a link para a documentação oficial do Java. Vamos avançar para os métodos terminais.

Métodos Terminais

  • O método terminal com o qual você já está familiarizado é o toList(). Ele converte o stream em uma lista e a retorna. Ou seja, é possível atribuir diretamente esse stream com métodos a uma lista. Este método foi introduzido no Java 17 e substitui a construção mais complexa collect(Collectors.toList());

  • O método collect() também converte o stream 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 executa 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 ele, 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 você pode não compreendê-lo imediatamente. No entanto, é um assunto que se domina com a prática. Nos próximos capítulos práticos sobre streams, haverá muitas oportunidades para trabalhar com eles, pois é uma forma muito conveniente e prática de manipular listas e arrays de dados!

1. Qual é o principal objetivo da Stream API em Java?

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

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

4. Como a operação flatMap é diferente de map na Stream API?

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

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

7. Qual das alternativas a seguir é uma operação intermediária na Stream API?

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

question mark

Qual é o principal objetivo da Stream API em Java?

Select the correct answer

question mark

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

Select the correct answer

question mark

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

Select the correct answer

question mark

Como a operação flatMap é diferente de map na Stream API?

Select the correct answer

question mark

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

Select the correct answer

question mark

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

Select the correct answer

question mark

Qual das alternativas a seguir é uma operação intermediária na Stream API?

Select the correct answer

question mark

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

Select the correct answer

Tudo estava claro?

Como podemos melhorá-lo?

Obrigado pelo seu feedback!

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