Exempel Från Verkligheten på Användning av Stream API
Kod handlar inte bara om funktionalitet—det handlar också om läsbarhet. Välstrukturerad kod är enklare att underhålla, modifiera och utöka.
Du är en utvecklare som har fått i uppgift att granska någon annans kod och göra den bättre. I verkliga projekt skrivs kod ofta i hast, kopieras från olika delar av programmet eller är helt enkelt inte utformad med läsbarhet i åtanke. Din uppgift är inte bara att förstå koden utan att förbättra den—göra den renare, mer koncis och enklare att underhålla.
Just nu genomför du en kodgranskning. Du kommer att analysera ett verkligt kodexempel, identifiera dess svagheter och refaktorera det steg för steg med hjälp av Stream API.
Kom igång
Föreställ dig att du har en webbutik och behöver identifiera aktiva användare som har lagt minst tre beställningar på $10,000 eller mer. Detta hjälper marknadsföringsteamet att känna igen de mest värdefulla kunderna och erbjuda dem personliga erbjudanden.
Här är ursprungskoden innan refaktorering:
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 + "'}"; } }
Du har två nyckelklasser:
Order representerar en order och innehåller ett fält total som lagrar orderbeloppet.
User representerar en butikskund med tre fält:
name(användarens namn);active(statusflagga);orders(lista med ordrar).
Varje User innehåller en lista av Order-objekt, vilket modellerar ett verkligt scenario där en kund lägger flera ordrar.
I main skapas en lista med användare, var och en med sitt eget uppsättning ordrar. Programmet itererar sedan genom denna lista och kontrollerar om en användare är aktiv. Om inte, hoppas de över.
Därefter loopar programmet genom användarens ordrar och räknar hur många som är 10 000 $ eller mer. Om användaren har minst tre kvalificerade ordrar läggs de till i listan premiumUsers.
När alla användare har behandlats skrivs premiumanvändarna ut.
Problem med koden
- För många nästlade loopar – gör det svårare att läsa och förstå;
- Redundant kod – för många manuella kontroller och mellanliggande variabler;
- Brist på deklarativ stil – koden känns som låg-nivå databehandling istället för hög-nivå logik.
Du kommer nu att refaktorera denna kod steg för steg med hjälp av Stream API för att förbättra läsbarheten, minska redundans och göra den mer effektiv.
Kodrefaktorering
Första steget är att ta bort den yttre if (user.isActive()) och integrera den direkt i Stream API:
List<User> premiumUsers = users.stream()
.filter(User::isActive) // Keep only active users
.toList();
Nu är koden mer deklarativ och visar tydligt att du filtrerar aktiva användare. Den onödiga if-satsen är borta—logiken är nu direkt inbyggd i Stream API. Detta är dock bara databeredning, så låt oss gå vidare!
Nästa steg är att ersätta den nästlade for-loopen (for (Order order : user.getOrders())) med en stream() inuti 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();
Genom att eliminera manuell räkning har koden blivit renare och mer lättläst—nu hanterar count() detta åt oss, vilket gör att vi kan arbeta med streamen utan extra variabler.
Slutgiltig omstrukturerad kod
Nu har du en fullt omstrukturerad lösning som löser uppgiften på ett deklarativt och koncist sätt:
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 + "'}"; } }
Koden är nu kortare och tydligare eftersom du, istället för en serie manuella kontroller, använder ett deklarativt tillvägagångssätt—fokus ligger på vad som ska göras snarare än att beskriva varje steg i processen. Detta eliminerar behovet av nästlade loopar, vilket gör koden lättare att läsa och underhålla.
Genom att utnyttja Stream API kombineras filtrering, räkning och datainsamling smidigt i en enda ström, vilket gör koden mer uttrycksfull och effektiv.
Tack för dina kommentarer!
Fråga AI
Fråga AI
Fråga vad du vill eller prova någon av de föreslagna frågorna för att starta vårt samtal
Awesome!
Completion rate improved to 2.33
Exempel Från Verkligheten på Användning av Stream API
Svep för att visa menyn
Kod handlar inte bara om funktionalitet—det handlar också om läsbarhet. Välstrukturerad kod är enklare att underhålla, modifiera och utöka.
Du är en utvecklare som har fått i uppgift att granska någon annans kod och göra den bättre. I verkliga projekt skrivs kod ofta i hast, kopieras från olika delar av programmet eller är helt enkelt inte utformad med läsbarhet i åtanke. Din uppgift är inte bara att förstå koden utan att förbättra den—göra den renare, mer koncis och enklare att underhålla.
Just nu genomför du en kodgranskning. Du kommer att analysera ett verkligt kodexempel, identifiera dess svagheter och refaktorera det steg för steg med hjälp av Stream API.
Kom igång
Föreställ dig att du har en webbutik och behöver identifiera aktiva användare som har lagt minst tre beställningar på $10,000 eller mer. Detta hjälper marknadsföringsteamet att känna igen de mest värdefulla kunderna och erbjuda dem personliga erbjudanden.
Här är ursprungskoden innan refaktorering:
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 + "'}"; } }
Du har två nyckelklasser:
Order representerar en order och innehåller ett fält total som lagrar orderbeloppet.
User representerar en butikskund med tre fält:
name(användarens namn);active(statusflagga);orders(lista med ordrar).
Varje User innehåller en lista av Order-objekt, vilket modellerar ett verkligt scenario där en kund lägger flera ordrar.
I main skapas en lista med användare, var och en med sitt eget uppsättning ordrar. Programmet itererar sedan genom denna lista och kontrollerar om en användare är aktiv. Om inte, hoppas de över.
Därefter loopar programmet genom användarens ordrar och räknar hur många som är 10 000 $ eller mer. Om användaren har minst tre kvalificerade ordrar läggs de till i listan premiumUsers.
När alla användare har behandlats skrivs premiumanvändarna ut.
Problem med koden
- För många nästlade loopar – gör det svårare att läsa och förstå;
- Redundant kod – för många manuella kontroller och mellanliggande variabler;
- Brist på deklarativ stil – koden känns som låg-nivå databehandling istället för hög-nivå logik.
Du kommer nu att refaktorera denna kod steg för steg med hjälp av Stream API för att förbättra läsbarheten, minska redundans och göra den mer effektiv.
Kodrefaktorering
Första steget är att ta bort den yttre if (user.isActive()) och integrera den direkt i Stream API:
List<User> premiumUsers = users.stream()
.filter(User::isActive) // Keep only active users
.toList();
Nu är koden mer deklarativ och visar tydligt att du filtrerar aktiva användare. Den onödiga if-satsen är borta—logiken är nu direkt inbyggd i Stream API. Detta är dock bara databeredning, så låt oss gå vidare!
Nästa steg är att ersätta den nästlade for-loopen (for (Order order : user.getOrders())) med en stream() inuti 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();
Genom att eliminera manuell räkning har koden blivit renare och mer lättläst—nu hanterar count() detta åt oss, vilket gör att vi kan arbeta med streamen utan extra variabler.
Slutgiltig omstrukturerad kod
Nu har du en fullt omstrukturerad lösning som löser uppgiften på ett deklarativt och koncist sätt:
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 + "'}"; } }
Koden är nu kortare och tydligare eftersom du, istället för en serie manuella kontroller, använder ett deklarativt tillvägagångssätt—fokus ligger på vad som ska göras snarare än att beskriva varje steg i processen. Detta eliminerar behovet av nästlade loopar, vilket gör koden lättare att läsa och underhålla.
Genom att utnyttja Stream API kombineras filtrering, räkning och datainsamling smidigt i en enda ström, vilket gör koden mer uttrycksfull och effektiv.
Tack för dina kommentarer!