Ejemplos del Mundo Real de Uso de Stream API
El código no solo trata de la funcionalidad—también se trata de la legibilidad. Un código bien estructurado es más fácil de mantener, modificar y ampliar.
Eres un desarrollador encargado de revisar el código de otra persona y mejorarlo. En proyectos reales, el código a menudo se escribe rápidamente, se copia de diferentes partes del programa o simplemente no se diseña pensando en la legibilidad. Tu tarea no es solo entender el código, sino también mejorarlo—haciéndolo más limpio, conciso y fácil de mantener.
En este momento, estás realizando una revisión de código. Vas a analizar un fragmento real de código, identificar sus puntos débiles y refactorizarlo paso a paso utilizando Stream API.
Comenzando
Imagina que tienes una tienda en línea y necesitas identificar a los usuarios activos que han realizado al menos tres pedidos de $10,000 o más. Esto ayudará al equipo de marketing a reconocer a los clientes más valiosos y ofrecerles ofertas personalizadas.
Aquí está el código inicial antes de la refactorización:
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 + "'}"; } }
Tienes dos clases clave:
Order representa un pedido y contiene un campo total que almacena el importe del pedido.
User representa un cliente de la tienda con tres campos:
name(nombre del usuario);active(indicador de estado);orders(lista de pedidos).
Cada User contiene una lista de objetos Order, modelando un escenario real donde un cliente realiza múltiples pedidos.
En main, se crea una lista de usuarios, cada uno con su propio conjunto de pedidos. El programa luego itera sobre esta lista y verifica si un usuario está activo. Si no lo está, se omite.
A continuación, el programa recorre los pedidos del usuario y cuenta cuántos son de $10,000 o más. Si el usuario tiene al menos tres pedidos que cumplen la condición, se añade a la lista premiumUsers.
Una vez que se han procesado todos los usuarios, el programa muestra los usuarios premium.
Problemas con el código
- Demasiados bucles anidados – dificulta la lectura y comprensión;
- Código redundante – demasiadas comprobaciones manuales y variables intermedias;
- Falta de estilo declarativo – el código se percibe como procesamiento de datos de bajo nivel en lugar de lógica de alto nivel.
Ahora vas a refactorizar este código paso a paso utilizando Stream API para mejorar la legibilidad, reducir la redundancia y hacerlo más eficiente.
Refactorización de código
El primer paso consiste en eliminar el if (user.isActive()) externo e integrarlo directamente en la Stream API:
List<User> premiumUsers = users.stream()
.filter(User::isActive) // Keep only active users
.toList();
Ahora, el código es más declarativo y muestra claramente que se están filtrando usuarios activos. La condición if innecesaria ha desaparecido—la lógica ahora está incorporada directamente en la Stream API. Sin embargo, esto es solo una preparación de datos, ¡así que continuemos!
A continuación, se debe reemplazar el bucle for anidado (for (Order order : user.getOrders())) por un stream() dentro del 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();
Al eliminar el conteo manual, el código es más limpio y legible—ahora, count() se encarga de esto, permitiendo trabajar con el stream sin variables adicionales.
Código refactorizado final
Ahora se dispone de una solución completamente refactorizada que resuelve la tarea de manera declarativa y 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 + "'}"; } }
El código ahora es más corto y claro porque, en lugar de una serie de comprobaciones manuales, se utiliza un enfoque declarativo—centrándose en lo que se debe hacer en lugar de detallar cada paso del proceso. Esto elimina la necesidad de bucles anidados, haciendo que el código sea más legible y mantenible.
Al aprovechar Stream API, se combinan de manera fluida la filtración, el conteo y la recolección de datos en un solo stream, haciendo que el código sea más expresivo y eficiente.
¡Gracias por tus comentarios!
Pregunte a AI
Pregunte a AI
Pregunte lo que quiera o pruebe una de las preguntas sugeridas para comenzar nuestra charla
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
Ejemplos del Mundo Real de Uso de Stream API
Desliza para mostrar el menú
El código no solo trata de la funcionalidad—también se trata de la legibilidad. Un código bien estructurado es más fácil de mantener, modificar y ampliar.
Eres un desarrollador encargado de revisar el código de otra persona y mejorarlo. En proyectos reales, el código a menudo se escribe rápidamente, se copia de diferentes partes del programa o simplemente no se diseña pensando en la legibilidad. Tu tarea no es solo entender el código, sino también mejorarlo—haciéndolo más limpio, conciso y fácil de mantener.
En este momento, estás realizando una revisión de código. Vas a analizar un fragmento real de código, identificar sus puntos débiles y refactorizarlo paso a paso utilizando Stream API.
Comenzando
Imagina que tienes una tienda en línea y necesitas identificar a los usuarios activos que han realizado al menos tres pedidos de $10,000 o más. Esto ayudará al equipo de marketing a reconocer a los clientes más valiosos y ofrecerles ofertas personalizadas.
Aquí está el código inicial antes de la refactorización:
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 + "'}"; } }
Tienes dos clases clave:
Order representa un pedido y contiene un campo total que almacena el importe del pedido.
User representa un cliente de la tienda con tres campos:
name(nombre del usuario);active(indicador de estado);orders(lista de pedidos).
Cada User contiene una lista de objetos Order, modelando un escenario real donde un cliente realiza múltiples pedidos.
En main, se crea una lista de usuarios, cada uno con su propio conjunto de pedidos. El programa luego itera sobre esta lista y verifica si un usuario está activo. Si no lo está, se omite.
A continuación, el programa recorre los pedidos del usuario y cuenta cuántos son de $10,000 o más. Si el usuario tiene al menos tres pedidos que cumplen la condición, se añade a la lista premiumUsers.
Una vez que se han procesado todos los usuarios, el programa muestra los usuarios premium.
Problemas con el código
- Demasiados bucles anidados – dificulta la lectura y comprensión;
- Código redundante – demasiadas comprobaciones manuales y variables intermedias;
- Falta de estilo declarativo – el código se percibe como procesamiento de datos de bajo nivel en lugar de lógica de alto nivel.
Ahora vas a refactorizar este código paso a paso utilizando Stream API para mejorar la legibilidad, reducir la redundancia y hacerlo más eficiente.
Refactorización de código
El primer paso consiste en eliminar el if (user.isActive()) externo e integrarlo directamente en la Stream API:
List<User> premiumUsers = users.stream()
.filter(User::isActive) // Keep only active users
.toList();
Ahora, el código es más declarativo y muestra claramente que se están filtrando usuarios activos. La condición if innecesaria ha desaparecido—la lógica ahora está incorporada directamente en la Stream API. Sin embargo, esto es solo una preparación de datos, ¡así que continuemos!
A continuación, se debe reemplazar el bucle for anidado (for (Order order : user.getOrders())) por un stream() dentro del 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();
Al eliminar el conteo manual, el código es más limpio y legible—ahora, count() se encarga de esto, permitiendo trabajar con el stream sin variables adicionales.
Código refactorizado final
Ahora se dispone de una solución completamente refactorizada que resuelve la tarea de manera declarativa y 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 + "'}"; } }
El código ahora es más corto y claro porque, en lugar de una serie de comprobaciones manuales, se utiliza un enfoque declarativo—centrándose en lo que se debe hacer en lugar de detallar cada paso del proceso. Esto elimina la necesidad de bucles anidados, haciendo que el código sea más legible y mantenible.
Al aprovechar Stream API, se combinan de manera fluida la filtración, el conteo y la recolección de datos en un solo stream, haciendo que el código sea más expresivo y eficiente.
¡Gracias por tus comentarios!