Virkelige Eksempler på Brug af Stream API
Kode handler ikke kun om funktionalitet—det handler også om læsbarhed. Velstruktureret kode er lettere at vedligeholde, ændre og udvide.
Du er en udvikler, der har til opgave at gennemgå en andens kode og gøre den bedre. I virkelige projekter bliver kode ofte skrevet i hast, kopieret fra forskellige dele af programmet eller blot ikke designet med læsbarhed for øje. Din opgave er ikke kun at forstå koden, men også at forbedre den—gøre den renere, mere kortfattet og lettere at vedligeholde.
Lige nu udfører du et kodegennemgang. Du vil analysere et reelt stykke kode, identificere dets svage punkter og refaktorere det trin for trin ved hjælp af Stream API.
Kom godt i gang
Forestil dig, at du har en onlinebutik, og du skal identificere aktive brugere, der har afgivet mindst tre ordrer på $10.000 eller mere. Dette vil hjælpe marketingteamet med at genkende de mest værdifulde kunder og tilbyde dem personlige tilbud.
Her er den oprindelige kode før 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 to nøgleklasser:
Order repræsenterer en ordre og indeholder et total-felt, der gemmer ordrens beløb.
User repræsenterer en butikskunde med tre felter:
name(brugerens navn);active(statusflag);orders(liste over ordrer).
Hver User indeholder en liste af Order-objekter, hvilket modellerer et virkelighedsnært scenarie, hvor en kunde afgiver flere ordrer.
I main oprettes en liste af brugere, hver med deres eget sæt af ordrer. Programmet gennemløber derefter denne liste og kontrollerer, om en bruger er aktiv. Hvis ikke, springes de over.
Dernæst gennemløber programmet brugerens ordrer og tæller, hvor mange der er på $10.000 eller mere. Hvis brugeren har mindst tre kvalificerende ordrer, tilføjes de til listen premiumUsers.
Når alle brugere er behandlet, udskriver programmet de premium-brugere.
Problemer med koden
- For mange indlejrede løkker – gør det sværere at læse og forstå;
- Redundant kode – for mange manuelle tjek og mellemliggende variabler;
- Mangel på deklarativ stil – koden føles som lavniveau databehandling i stedet for høj-niveau logik.
Du vil nu refaktorere denne kode trin for trin ved hjælp af Stream API for at forbedre læsbarheden, reducere redundans og gøre den mere effektiv.
Kodeomstrukturering
Første trin er at fjerne den ydre if (user.isActive()) og integrere den direkte i Stream API:
List<User> premiumUsers = users.stream()
.filter(User::isActive) // Keep only active users
.toList();
Nu er koden mere deklarativ og viser tydeligt, at du filtrerer aktive brugere. Den unødvendige if-betingelse er fjernet—logikken er nu bygget direkte ind i Stream API. Dette er dog kun dataklargøring, så lad os gå videre!
Dernæst skal vi erstatte den indlejrede for-løkke (for (Order order : user.getOrders())) med et stream() inde i 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();
Ved at eliminere manuel optælling er koden blevet renere og mere læsbar—nu håndterer count() dette for os, hvilket gør det muligt at arbejde med streamen uden ekstra variabler.
Endelig refaktoreret kode
Nu foreligger en fuldt refaktoreret løsning, der løser opgaven på en deklarativ og kortfattet måde:
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 er nu kortere og mere overskuelig, fordi du i stedet for en række manuelle kontroller anvender en deklarativ tilgang—med fokus på, hvad der skal gøres, frem for at beskrive hvert enkelt trin i processen. Dette eliminerer behovet for indlejrede løkker, hvilket gør koden lettere at læse og vedligeholde.
Ved at udnytte Stream API kombineres filtrering, optælling og datainnsamling problemfrit i én enkelt stream, hvilket gør koden mere udtryksfuld og effektiv.
Tak for dine kommentarer!
Spørg AI
Spørg AI
Spørg om hvad som helst eller prøv et af de foreslåede spørgsmål for at starte vores chat
Awesome!
Completion rate improved to 2.33
Virkelige Eksempler på Brug af Stream API
Stryg for at vise menuen
Kode handler ikke kun om funktionalitet—det handler også om læsbarhed. Velstruktureret kode er lettere at vedligeholde, ændre og udvide.
Du er en udvikler, der har til opgave at gennemgå en andens kode og gøre den bedre. I virkelige projekter bliver kode ofte skrevet i hast, kopieret fra forskellige dele af programmet eller blot ikke designet med læsbarhed for øje. Din opgave er ikke kun at forstå koden, men også at forbedre den—gøre den renere, mere kortfattet og lettere at vedligeholde.
Lige nu udfører du et kodegennemgang. Du vil analysere et reelt stykke kode, identificere dets svage punkter og refaktorere det trin for trin ved hjælp af Stream API.
Kom godt i gang
Forestil dig, at du har en onlinebutik, og du skal identificere aktive brugere, der har afgivet mindst tre ordrer på $10.000 eller mere. Dette vil hjælpe marketingteamet med at genkende de mest værdifulde kunder og tilbyde dem personlige tilbud.
Her er den oprindelige kode før 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 to nøgleklasser:
Order repræsenterer en ordre og indeholder et total-felt, der gemmer ordrens beløb.
User repræsenterer en butikskunde med tre felter:
name(brugerens navn);active(statusflag);orders(liste over ordrer).
Hver User indeholder en liste af Order-objekter, hvilket modellerer et virkelighedsnært scenarie, hvor en kunde afgiver flere ordrer.
I main oprettes en liste af brugere, hver med deres eget sæt af ordrer. Programmet gennemløber derefter denne liste og kontrollerer, om en bruger er aktiv. Hvis ikke, springes de over.
Dernæst gennemløber programmet brugerens ordrer og tæller, hvor mange der er på $10.000 eller mere. Hvis brugeren har mindst tre kvalificerende ordrer, tilføjes de til listen premiumUsers.
Når alle brugere er behandlet, udskriver programmet de premium-brugere.
Problemer med koden
- For mange indlejrede løkker – gør det sværere at læse og forstå;
- Redundant kode – for mange manuelle tjek og mellemliggende variabler;
- Mangel på deklarativ stil – koden føles som lavniveau databehandling i stedet for høj-niveau logik.
Du vil nu refaktorere denne kode trin for trin ved hjælp af Stream API for at forbedre læsbarheden, reducere redundans og gøre den mere effektiv.
Kodeomstrukturering
Første trin er at fjerne den ydre if (user.isActive()) og integrere den direkte i Stream API:
List<User> premiumUsers = users.stream()
.filter(User::isActive) // Keep only active users
.toList();
Nu er koden mere deklarativ og viser tydeligt, at du filtrerer aktive brugere. Den unødvendige if-betingelse er fjernet—logikken er nu bygget direkte ind i Stream API. Dette er dog kun dataklargøring, så lad os gå videre!
Dernæst skal vi erstatte den indlejrede for-løkke (for (Order order : user.getOrders())) med et stream() inde i 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();
Ved at eliminere manuel optælling er koden blevet renere og mere læsbar—nu håndterer count() dette for os, hvilket gør det muligt at arbejde med streamen uden ekstra variabler.
Endelig refaktoreret kode
Nu foreligger en fuldt refaktoreret løsning, der løser opgaven på en deklarativ og kortfattet måde:
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 er nu kortere og mere overskuelig, fordi du i stedet for en række manuelle kontroller anvender en deklarativ tilgang—med fokus på, hvad der skal gøres, frem for at beskrive hvert enkelt trin i processen. Dette eliminerer behovet for indlejrede løkker, hvilket gør koden lettere at læse og vedligeholde.
Ved at udnytte Stream API kombineres filtrering, optælling og datainnsamling problemfrit i én enkelt stream, hvilket gør koden mere udtryksfuld og effektiv.
Tak for dine kommentarer!