Desempenho na Stream API
Você refatorou o código com sucesso, tornando-o mais limpo, mais curto e mais expressivo.
Mas e quanto ao desempenho? Quão eficiente é a Stream API em comparação com os laços tradicionais? É possível acelerar utilizando parallelStream()? Vamos descobrir!
Medindo o Tempo de Execução
Para comparar desempenho de forma objetiva, será criado um conjunto de testes com 100.000 usuários e diferentes pedidos. Em seguida, serão implementadas três abordagens de filtragem:
for-looptradicional – abordagem clássica com laços aninhados;- Stream API (
stream()) – método declarativo moderno; - Parallel Stream API (
parallelStream()) – processamento multithread.
O tempo de execução será medido utilizando System.nanoTime(), que fornece diferenças de tempo de alta precisão.
Implementação do Teste
Serão gerados 100.000 usuários, garantindo que todos possuam pedidos acima de $10,000, e todas as três abordagens serão executadas para verificar qual apresenta o melhor desempenho.
Main.java
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100package com.example; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { List<User> users = generateUsers(100000); // Measuring traditional loop execution time long startTime = System.nanoTime(); List<User> premiumUsersLoop = 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) { premiumUsersLoop.add(user); } } } long endTime = System.nanoTime(); System.out.println("For-loop: " + (endTime - startTime) / 1_000_000 + " ms"); // Measuring Stream API execution time startTime = System.nanoTime(); List<User> premiumUsersStream = users.stream() .filter(User::isActive) .filter(user -> user.getOrders().stream() .filter(order -> order.getTotal() >= 10000) .count() >= 3) .toList(); endTime = System.nanoTime(); System.out.println("Stream API: " + (endTime - startTime) / 1_000_000 + " ms"); // Measuring Parallel Stream API execution time startTime = System.nanoTime(); List<User> premiumUsersParallelStream = users.parallelStream() .filter(User::isActive) .filter(user -> user.getOrders().stream() .filter(order -> order.getTotal() >= 10000) .count() >= 3) .toList(); endTime = System.nanoTime(); System.out.println("Parallel Stream API: " + (endTime - startTime) / 1_000_000 + " ms"); } private static List<User> generateUsers(int count) { List<User> users = new ArrayList<>(); for (int i = 0; i < count; i++) { users.add(new User("User" + i, true, List.of( new Order(14000), new Order(5000), new Order(7000) ))); } return users; } } 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 + "'}"; } }
Após executar o teste com 100.000 usuários, é possível observar as seguintes tendências:
O for-loop tradicional é mais rápido para operações simples, pois evita a sobrecarga da criação de streams e objetos adicionais. Apresenta melhor desempenho quando a velocidade bruta é prioridade.
A Stream API às vezes é um pouco mais lenta devido à criação de objetos adicionais, mas melhora significativamente a legibilidade e a manutenibilidade do código.
A Parallel Stream API pode acelerar o processamento em sistemas multi-core, mas nem sempre. Se o conjunto de dados for pequeno, a sobrecarga de gerenciamento de threads pode realmente diminuir a velocidade. Apresenta melhor desempenho em cálculos pesados, mas não ao modificar variáveis compartilhadas, pois as threads são executadas de forma independente.
Resumo
A Stream API é uma ferramenta poderosa que torna o código mais legível e conciso. No entanto, quando se trata de desempenho, é importante compreender suas limitações. Em alguns casos, laços for tradicionais são mais rápidos, especialmente ao trabalhar com conjuntos de dados pequenos. O parallelStream() pode melhorar a velocidade de processamento, mas requer testes para garantir que realmente oferece um benefício.
Portanto, a escolha da abordagem correta deve ser intencional: se a legibilidade for prioridade, utilize a Stream API; se o desempenho for crítico, teste e meça!
1. Qual abordagem é normalmente a mais rápida para operações simples?
2. Por que a Stream API pode ser mais lenta do que um laço regular?
3. Quando o parallelStream() pode diminuir a velocidade da execução?
Obrigado pelo seu feedback!
Pergunte à IA
Pergunte à IA
Pergunte o que quiser ou experimente uma das perguntas sugeridas para iniciar nosso bate-papo
Awesome!
Completion rate improved to 2.33
Desempenho na Stream API
Deslize para mostrar o menu
Você refatorou o código com sucesso, tornando-o mais limpo, mais curto e mais expressivo.
Mas e quanto ao desempenho? Quão eficiente é a Stream API em comparação com os laços tradicionais? É possível acelerar utilizando parallelStream()? Vamos descobrir!
Medindo o Tempo de Execução
Para comparar desempenho de forma objetiva, será criado um conjunto de testes com 100.000 usuários e diferentes pedidos. Em seguida, serão implementadas três abordagens de filtragem:
for-looptradicional – abordagem clássica com laços aninhados;- Stream API (
stream()) – método declarativo moderno; - Parallel Stream API (
parallelStream()) – processamento multithread.
O tempo de execução será medido utilizando System.nanoTime(), que fornece diferenças de tempo de alta precisão.
Implementação do Teste
Serão gerados 100.000 usuários, garantindo que todos possuam pedidos acima de $10,000, e todas as três abordagens serão executadas para verificar qual apresenta o melhor desempenho.
Main.java
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100package com.example; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { List<User> users = generateUsers(100000); // Measuring traditional loop execution time long startTime = System.nanoTime(); List<User> premiumUsersLoop = 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) { premiumUsersLoop.add(user); } } } long endTime = System.nanoTime(); System.out.println("For-loop: " + (endTime - startTime) / 1_000_000 + " ms"); // Measuring Stream API execution time startTime = System.nanoTime(); List<User> premiumUsersStream = users.stream() .filter(User::isActive) .filter(user -> user.getOrders().stream() .filter(order -> order.getTotal() >= 10000) .count() >= 3) .toList(); endTime = System.nanoTime(); System.out.println("Stream API: " + (endTime - startTime) / 1_000_000 + " ms"); // Measuring Parallel Stream API execution time startTime = System.nanoTime(); List<User> premiumUsersParallelStream = users.parallelStream() .filter(User::isActive) .filter(user -> user.getOrders().stream() .filter(order -> order.getTotal() >= 10000) .count() >= 3) .toList(); endTime = System.nanoTime(); System.out.println("Parallel Stream API: " + (endTime - startTime) / 1_000_000 + " ms"); } private static List<User> generateUsers(int count) { List<User> users = new ArrayList<>(); for (int i = 0; i < count; i++) { users.add(new User("User" + i, true, List.of( new Order(14000), new Order(5000), new Order(7000) ))); } return users; } } 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 + "'}"; } }
Após executar o teste com 100.000 usuários, é possível observar as seguintes tendências:
O for-loop tradicional é mais rápido para operações simples, pois evita a sobrecarga da criação de streams e objetos adicionais. Apresenta melhor desempenho quando a velocidade bruta é prioridade.
A Stream API às vezes é um pouco mais lenta devido à criação de objetos adicionais, mas melhora significativamente a legibilidade e a manutenibilidade do código.
A Parallel Stream API pode acelerar o processamento em sistemas multi-core, mas nem sempre. Se o conjunto de dados for pequeno, a sobrecarga de gerenciamento de threads pode realmente diminuir a velocidade. Apresenta melhor desempenho em cálculos pesados, mas não ao modificar variáveis compartilhadas, pois as threads são executadas de forma independente.
Resumo
A Stream API é uma ferramenta poderosa que torna o código mais legível e conciso. No entanto, quando se trata de desempenho, é importante compreender suas limitações. Em alguns casos, laços for tradicionais são mais rápidos, especialmente ao trabalhar com conjuntos de dados pequenos. O parallelStream() pode melhorar a velocidade de processamento, mas requer testes para garantir que realmente oferece um benefício.
Portanto, a escolha da abordagem correta deve ser intencional: se a legibilidade for prioridade, utilize a Stream API; se o desempenho for crítico, teste e meça!
1. Qual abordagem é normalmente a mais rápida para operações simples?
2. Por que a Stream API pode ser mais lenta do que um laço regular?
3. Quando o parallelStream() pode diminuir a velocidade da execução?
Obrigado pelo seu feedback!