Exemplos do Mundo Real de Uso da Stream API
Código não se trata apenas de funcionalidade—também envolve legibilidade. Código bem estruturado é mais fácil de manter, modificar e expandir.
Você é um desenvolvedor responsável por revisar o código de outra pessoa e torná-lo melhor. Em projetos reais, o código muitas vezes é escrito às pressas, copiado de diferentes partes do programa ou simplesmente não foi criado com a legibilidade em mente. Sua tarefa não é apenas entender o código, mas também melhorá-lo—tornando-o mais limpo, conciso e fácil de manter.
Neste momento, você está realizando uma revisão de código. Você irá analisar um trecho real de código, identificar seus pontos fracos e refatorá-lo passo a passo utilizando a Stream API.
Introdução
Imagine que você possui uma loja online e precisa identificar usuários ativos que realizaram pelo menos três pedidos de $10,000 ou mais. Isso ajudará a equipe de marketing a reconhecer os clientes mais valiosos e oferecer ofertas personalizadas.
Veja o código inicial antes da refatoração:
Main.java
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970package com.example; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { List<User> users = List.of( new User("Alice", true, List.of(new Order(12000), new Order(15000), new Order(11000))), new User("Bob", true, List.of(new Order(8000), new Order(9000), new Order(12000))), new User("Charlie", false, List.of(new Order(15000), new Order(16000), new Order(17000))), new User("David", true, List.of(new Order(5000), new Order(20000), new Order(30000))), new User("Eve", true, List.of(new Order(10000), new Order(10000), new Order(10000), new Order(12000))) ); List<User> premiumUsers = new ArrayList<>(); for (User user : users) { if (user.isActive()) { int totalOrders = 0; for (Order order : user.getOrders()) { if (order.getTotal() >= 10000) { totalOrders++; } } if (totalOrders >= 3) { premiumUsers.add(user); } } } System.out.println("Premium users: " + premiumUsers); } } class Order { private final double total; public Order(double total) { this.total = total; } public double getTotal() { return total; } } class User { private final String name; private final boolean active; private final List<Order> orders; public User(String name, boolean active, List<Order> orders) { this.name = name; this.active = active; this.orders = orders; } public boolean isActive() { return active; } public List<Order> getOrders() { return orders; } @Override public String toString() { return "User{name='" + name + "'}"; } }
Você possui duas classes principais:
Order representa um pedido e contém um campo total que armazena o valor do pedido.
User representa um cliente da loja com três campos:
name(nome do usuário);active(indicador de status);orders(lista de pedidos).
Cada User contém uma lista de objetos Order, modelando um cenário real onde um cliente realiza múltiplos pedidos.
No main, você cria uma lista de usuários, cada um com seu próprio conjunto de pedidos. O programa então itera por essa lista e verifica se o usuário está ativo. Caso contrário, ele é ignorado.
Em seguida, o programa percorre os pedidos do usuário e conta quantos são de $10.000 ou mais. Se o usuário tiver pelo menos três pedidos qualificados, ele é adicionado à lista premiumUsers.
Após todos os usuários serem processados, o programa exibe os usuários premium.
Problemas com o Código
- Muitos loops aninhados – dificulta a leitura e compreensão;
- Código redundante – excesso de verificações manuais e variáveis intermediárias;
- Falta de estilo declarativo – o código parece processamento de dados de baixo nível em vez de lógica de alto nível.
Agora você irá refatorar este código passo a passo utilizando a Stream API para melhorar a legibilidade, reduzir redundâncias e torná-lo mais eficiente.
Refatoração de Código
O primeiro passo é remover o if (user.isActive()) externo e integrá-lo diretamente na Stream API:
List<User> premiumUsers = users.stream()
.filter(User::isActive) // Keep only active users
.toList();
Agora, o código está mais declarativo e mostra claramente que está ocorrendo um filtro de usuários ativos. A condição if desnecessária foi removida—a lógica agora está incorporada diretamente na Stream API. No entanto, isso é apenas uma preparação de dados, então vamos avançar!
Em seguida, vamos substituir o for aninhado (for (Order order : user.getOrders())) por um stream() dentro do filter:
List<User> premiumUsers = users.stream()
.filter(User::isActive)
.filter(user -> user.getOrders().stream()
.filter(order -> order.getTotal() >= 10000)
.count() >= 3) // Count orders directly in Stream API
.toList();
Ao eliminar a contagem manual, o código se torna mais limpo e legível—agora, count() realiza essa tarefa, permitindo trabalhar com o stream sem variáveis extras.
Código Final Refatorado
Agora, há uma solução totalmente refatorada que resolve a tarefa de forma declarativa e concisa:
Main.java
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<User> users = List.of( new User("Alice", true, List.of(new Order(12000), new Order(15000), new Order(11000))), new User("Bob", true, List.of(new Order(8000), new Order(9000), new Order(12000))), new User("Charlie", false, List.of(new Order(15000), new Order(16000), new Order(17000))), new User("David", true, List.of(new Order(5000), new Order(20000), new Order(30000))), new User("Eve", true, List.of(new Order(10000), new Order(10000), new Order(10000), new Order(12000))) ); List<User> premiumUsers = users.stream() .filter(User::isActive) .filter(user -> user.getOrders().stream() .filter(order -> order.getTotal() >= 10000) .count() >= 3) .toList(); System.out.println("Premium users: " + premiumUsers); } } class Order { private final double total; public Order(double total) { this.total = total; } public double getTotal() { return total; } } class User { private final String name; private final boolean active; private final List<Order> orders; public User(String name, boolean active, List<Order> orders) { this.name = name; this.active = active; this.orders = orders; } public boolean isActive() { return active; } public List<Order> getOrders() { return orders; } @Override public String toString() { return "User{name='" + name + "'}"; } }
O código agora está mais curto e claro porque, em vez de uma série de verificações manuais, utiliza-se uma abordagem declarativa—focando no que precisa ser feito ao invés de detalhar cada etapa do processo. Isso elimina a necessidade de laços aninhados, tornando o código mais fácil de ler e manter.
Ao utilizar o Stream API, é possível combinar de forma eficiente filtragem, contagem e coleta de dados em um único stream, tornando o código mais expressivo e eficiente.
Obrigado pelo seu feedback!
Pergunte à IA
Pergunte à IA
Pergunte o que quiser ou experimente uma das perguntas sugeridas para iniciar nosso bate-papo
Can you explain how the Stream API improves code readability in this example?
What are some potential drawbacks of using the Stream API in this scenario?
Can you show how the original (non-refactored) code looked for comparison?
Awesome!
Completion rate improved to 2.33
Exemplos do Mundo Real de Uso da Stream API
Deslize para mostrar o menu
Código não se trata apenas de funcionalidade—também envolve legibilidade. Código bem estruturado é mais fácil de manter, modificar e expandir.
Você é um desenvolvedor responsável por revisar o código de outra pessoa e torná-lo melhor. Em projetos reais, o código muitas vezes é escrito às pressas, copiado de diferentes partes do programa ou simplesmente não foi criado com a legibilidade em mente. Sua tarefa não é apenas entender o código, mas também melhorá-lo—tornando-o mais limpo, conciso e fácil de manter.
Neste momento, você está realizando uma revisão de código. Você irá analisar um trecho real de código, identificar seus pontos fracos e refatorá-lo passo a passo utilizando a Stream API.
Introdução
Imagine que você possui uma loja online e precisa identificar usuários ativos que realizaram pelo menos três pedidos de $10,000 ou mais. Isso ajudará a equipe de marketing a reconhecer os clientes mais valiosos e oferecer ofertas personalizadas.
Veja o código inicial antes da refatoração:
Main.java
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970package com.example; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { List<User> users = List.of( new User("Alice", true, List.of(new Order(12000), new Order(15000), new Order(11000))), new User("Bob", true, List.of(new Order(8000), new Order(9000), new Order(12000))), new User("Charlie", false, List.of(new Order(15000), new Order(16000), new Order(17000))), new User("David", true, List.of(new Order(5000), new Order(20000), new Order(30000))), new User("Eve", true, List.of(new Order(10000), new Order(10000), new Order(10000), new Order(12000))) ); List<User> premiumUsers = new ArrayList<>(); for (User user : users) { if (user.isActive()) { int totalOrders = 0; for (Order order : user.getOrders()) { if (order.getTotal() >= 10000) { totalOrders++; } } if (totalOrders >= 3) { premiumUsers.add(user); } } } System.out.println("Premium users: " + premiumUsers); } } class Order { private final double total; public Order(double total) { this.total = total; } public double getTotal() { return total; } } class User { private final String name; private final boolean active; private final List<Order> orders; public User(String name, boolean active, List<Order> orders) { this.name = name; this.active = active; this.orders = orders; } public boolean isActive() { return active; } public List<Order> getOrders() { return orders; } @Override public String toString() { return "User{name='" + name + "'}"; } }
Você possui duas classes principais:
Order representa um pedido e contém um campo total que armazena o valor do pedido.
User representa um cliente da loja com três campos:
name(nome do usuário);active(indicador de status);orders(lista de pedidos).
Cada User contém uma lista de objetos Order, modelando um cenário real onde um cliente realiza múltiplos pedidos.
No main, você cria uma lista de usuários, cada um com seu próprio conjunto de pedidos. O programa então itera por essa lista e verifica se o usuário está ativo. Caso contrário, ele é ignorado.
Em seguida, o programa percorre os pedidos do usuário e conta quantos são de $10.000 ou mais. Se o usuário tiver pelo menos três pedidos qualificados, ele é adicionado à lista premiumUsers.
Após todos os usuários serem processados, o programa exibe os usuários premium.
Problemas com o Código
- Muitos loops aninhados – dificulta a leitura e compreensão;
- Código redundante – excesso de verificações manuais e variáveis intermediárias;
- Falta de estilo declarativo – o código parece processamento de dados de baixo nível em vez de lógica de alto nível.
Agora você irá refatorar este código passo a passo utilizando a Stream API para melhorar a legibilidade, reduzir redundâncias e torná-lo mais eficiente.
Refatoração de Código
O primeiro passo é remover o if (user.isActive()) externo e integrá-lo diretamente na Stream API:
List<User> premiumUsers = users.stream()
.filter(User::isActive) // Keep only active users
.toList();
Agora, o código está mais declarativo e mostra claramente que está ocorrendo um filtro de usuários ativos. A condição if desnecessária foi removida—a lógica agora está incorporada diretamente na Stream API. No entanto, isso é apenas uma preparação de dados, então vamos avançar!
Em seguida, vamos substituir o for aninhado (for (Order order : user.getOrders())) por um stream() dentro do filter:
List<User> premiumUsers = users.stream()
.filter(User::isActive)
.filter(user -> user.getOrders().stream()
.filter(order -> order.getTotal() >= 10000)
.count() >= 3) // Count orders directly in Stream API
.toList();
Ao eliminar a contagem manual, o código se torna mais limpo e legível—agora, count() realiza essa tarefa, permitindo trabalhar com o stream sem variáveis extras.
Código Final Refatorado
Agora, há uma solução totalmente refatorada que resolve a tarefa de forma declarativa e concisa:
Main.java
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<User> users = List.of( new User("Alice", true, List.of(new Order(12000), new Order(15000), new Order(11000))), new User("Bob", true, List.of(new Order(8000), new Order(9000), new Order(12000))), new User("Charlie", false, List.of(new Order(15000), new Order(16000), new Order(17000))), new User("David", true, List.of(new Order(5000), new Order(20000), new Order(30000))), new User("Eve", true, List.of(new Order(10000), new Order(10000), new Order(10000), new Order(12000))) ); List<User> premiumUsers = users.stream() .filter(User::isActive) .filter(user -> user.getOrders().stream() .filter(order -> order.getTotal() >= 10000) .count() >= 3) .toList(); System.out.println("Premium users: " + premiumUsers); } } class Order { private final double total; public Order(double total) { this.total = total; } public double getTotal() { return total; } } class User { private final String name; private final boolean active; private final List<Order> orders; public User(String name, boolean active, List<Order> orders) { this.name = name; this.active = active; this.orders = orders; } public boolean isActive() { return active; } public List<Order> getOrders() { return orders; } @Override public String toString() { return "User{name='" + name + "'}"; } }
O código agora está mais curto e claro porque, em vez de uma série de verificações manuais, utiliza-se uma abordagem declarativa—focando no que precisa ser feito ao invés de detalhar cada etapa do processo. Isso elimina a necessidade de laços aninhados, tornando o código mais fácil de ler e manter.
Ao utilizar o Stream API, é possível combinar de forma eficiente filtragem, contagem e coleta de dados em um único stream, tornando o código mais expressivo e eficiente.
Obrigado pelo seu feedback!