API 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()eparallelStream()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
12List<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
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
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
1234567891011121314package 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
12345678910111213package 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
1234567891011121314package 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
123456789101112131415package 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
12345678910111213141516package 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
12345678910111213141516package 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
123456789101112131415161718192021222324package 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
12345678910111213141516171819202122232425262728293031323334package 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
sortedordena 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 primeirosnelementos 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étodolimit(n), que geralmente limita a quantidade de elementos no stream. Mesmo que criemos um stream com 1000 elementos e depois utilizemoslimit(200), o stream conterá apenas os primeiros 200 elementos.
Main.java
123456789101112package 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 complexacollect(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 interfaceCollectors. Esta interface possui métodos comotoList(),toSet()etoCollection(). Por exemplo:
Main.java
123456789101112131415package 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
1234567891011package 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?
Obrigado pelo seu feedback!
Pergunte à IA
Pergunte à IA
Pergunte o que quiser ou experimente uma das perguntas sugeridas para iniciar nosso bate-papo
Incrível!
Completion taxa melhorada para 4
API 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()eparallelStream()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
12List<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
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
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
1234567891011121314package 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
12345678910111213package 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
1234567891011121314package 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
123456789101112131415package 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
12345678910111213141516package 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
12345678910111213141516package 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
123456789101112131415161718192021222324package 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
12345678910111213141516171819202122232425262728293031323334package 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
sortedordena 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 primeirosnelementos 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étodolimit(n), que geralmente limita a quantidade de elementos no stream. Mesmo que criemos um stream com 1000 elementos e depois utilizemoslimit(200), o stream conterá apenas os primeiros 200 elementos.
Main.java
123456789101112package 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 complexacollect(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 interfaceCollectors. Esta interface possui métodos comotoList(),toSet()etoCollection(). Por exemplo:
Main.java
123456789101112131415package 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
1234567891011package 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?
Obrigado pelo seu feedback!