Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Lære Collect() Samler Streamelementer i en Samling | Terminale Operationer i Stream API'et
Stream API

bookCollect() Samler Streamelementer i en Samling

Du er allerede bekendt med terminale operationer og har endda brugt dem i tidligere eksempler og øvelser. Nu er det tid til at se nærmere på, hvordan de fungerer. Først ser vi på collect()-metoden, som er en af de vigtigste terminale operationer i Stream API.

Metoden collect()

Det er et af de mest kraftfulde værktøjer ved arbejde med streams, da det gør det muligt at akkumulere resultater i en List, Set eller Map, samt udføre komplekse grupperinger og statistiske beregninger.

Der findes to implementeringer af collect()-metoden—lad os undersøge begge.

Brug af collect() med funktionelle grænseflader

Metoden collect() i Stream API kan anvendes med tre funktionelle grænseflader for at give fuld kontrol over datainnsamling:

  • Supplier<R> supplier – opretter en tom samling (R), hvor elementer vil blive gemt. For eksempel initialiserer ArrayList::new en ny liste;
  • BiConsumer<R, ? super T> accumulator – tilføjer stream-elementer (T) til samlingen (R). For eksempel tilføjer List::add elementer til en liste;
  • BiConsumer<R, R> combiner – sammenfletter to samlinger ved parallel behandling. For eksempel kombinerer List::addAll lister til én.

Alle tre komponenter arbejder sammen for at give fleksibilitet i datainnsamling. Først opretter supplier en tom samling, der bruges til at akkumulere elementer fra streamen. Derefter tilføjer accumulator hvert element, efterhånden som streamen behandles. Dette forløb forbliver enkelt i en sekventiel stream.

Dog bliver det mere komplekst, når der arbejdes med parallelle streams (parallelStream()).

Databehandling er fordelt på flere tråde, hvor hver tråd opretter sin egen separate samling. Når behandlingen er færdig, skal disse individuelle samlinger flettes til et enkelt resultat. Her kommer combiner ind i billedet og kombinerer effektivt de separate dele til én samlet samling.

Praktisk eksempel

Du arbejder for en onlinebutik og har en liste over produkter. Din opgave er at indsamle kun de produkter, der koster mere end $500, ved hjælp af collect()-metoden med tre parametre.

Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
package com.example; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { // Initial list of products List<Product> productList = List.of( new Product("Laptop", 1200.99), new Product("Phone", 599.49), new Product("Headphones", 199.99), new Product("Monitor", 299.99), new Product("Tablet", 699.99) ); // Filtering and collecting products over $500 using `collect()` List<Product> expensiveProducts = productList.parallelStream() .filter(product -> product.getPrice() > 500) // Keep only expensive products .collect( ArrayList::new, // Create a new list (list, product) -> list.add(product), // Add each product to the list ArrayList::addAll // Merge lists (if the stream is parallel) ); // Print the result System.out.print("Products over $500: " + expensiveProducts); } } class Product { private String name; private double price; Product(String name, double price) { this.name = name; this.price = price; } public String getName() { return name; } public double getPrice() { return price; } @Override public String toString() { return name + " ($" + price + ")"; } }

Metoden collect() tager tre argumenter, som hver definerer et trin i indsamlingen af elementer til en liste:

  • ArrayList::new (Supplier) → opretter en tom ArrayList<Product> til at gemme resultaterne;

  • (list, product) -> list.add(product) (BiConsumer) → tilføjer hver Product til listen, hvis den opfylder filterbetingelsen (price > 500);

  • ArrayList::addAll (BiConsumer) → samler flere lister ved brug af parallelle streams og sikrer, at alle filtrerede produkter kombineres i en enkelt liste.

Selvom den tredje parameter primært bruges til parallel behandling, er den påkrævet af collect().

Brug af collect() med Collector-interfacet

Ud over at arbejde med tre funktionelle interfaces kan metoden collect() i Stream API også bruges med foruddefinerede implementeringer af Collector-interfacet.

Denne tilgang er mere fleksibel og bekvem, da den tilbyder indbyggede metoder til at arbejde med samlinger.

Collector<T, A, R>-interfacet består af flere centrale metoder:

  • Supplier<A> supplier() – opretter en tom beholder til at akkumulere elementer;
  • BiConsumer<A, T> accumulator() – definerer, hvordan elementer tilføjes til beholderen;
  • BinaryOperator<A> combiner() – samler to beholdere ved parallel behandling;
  • Function<A, R> finisher() – omdanner beholderen til det endelige resultat.

Som du kan se, ligner denne struktur den collect()-metode, der arbejder med funktionelle grænseflader, men den introducerer finisher()-metoden. Dette ekstra trin muliggør yderligere behandling af de indsamlede data, før det endelige resultat returneres—f.eks. sortering af listen inden returnering.

Derudover tilbyder Collector-grænsefladen metoden characteristics(), som definerer egenskaber, der hjælper med at optimere stream-udførelse:

Disse egenskaber hjælper Stream API med at optimere ydelsen. For eksempel, hvis en samling er iboende uordnet, kan angivelse af UNORDERED forhindre unødvendig sortering, hvilket gør operationen mere effektiv.

Praktisk eksempel

Forestil dig, at du driver en onlinebutik og skal behandle produktpriser inden indsamling. For eksempel ønsker du at afrunde hver pris til nærmeste hele tal, fjerne dubletter og sortere den endelige liste.

Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
package com.example; import java.util.*; import java.util.function.*; import java.util.stream.Collector; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<Double> prices = List.of(1200.99, 599.49, 199.99, 599.49, 1200.49, 200.0); // Using a custom `Collector` to round prices, remove duplicates, and sort List<Integer> processedPrices = prices.parallelStream() .collect(new RoundedSortedCollector()); System.out.println("Processed product prices: " + processedPrices); } } // Custom `Collector` that rounds prices, removes duplicates, and sorts them class RoundedSortedCollector implements Collector<Double, Set<Integer>, List<Integer>> { @Override public Supplier<Set<Integer>> supplier() { // Creates a `HashSet` to store unique rounded values return HashSet::new; } @Override public BiConsumer<Set<Integer>, Double> accumulator() { // Rounds price and adds to the set return (set, price) -> set.add((int) Math.round(price)); } @Override public BinaryOperator<Set<Integer>> combiner() { return (set1, set2) -> { set1.addAll(set2); // Merges two sets return set1; }; } @Override public Function<Set<Integer>, List<Integer>> finisher() { return set -> set.stream() .sorted() // Sorts the final list .toList(); } @Override public Set<Characteristics> characteristics() { // Order is not important during accumulation return Set.of(Characteristics.UNORDERED); } }

Databehandlingen indledes ved at sende dataene ind i en brugerdefineret Collector kaldet RoundedSortedCollector.

Denne collector akkumulerer først alle priser i et Set<Integer>, hvilket sikrer, at dubletter automatisk fjernes. Før hver værdi tilføjes, afrundes prisen ved hjælp af Math.round(price) og konverteres til en int. For eksempel vil både 1200.99 og 1200.49 blive til 1200, mens 199.99 afrundes til 200.

Hvis strømmen kører i paralleltilstand, sammenfletter combiner()-metoden to sæt ved at tilføje alle elementer fra det ene sæt til det andet. Dette trin er afgørende i multitrådede miljøer.

I den afsluttende fase, efter at alle priser er indsamlet, omdanner finisher()-metoden sættet til en sorteret liste. Den konverterer Set<Integer> til en stream, anvender sorted() for at arrangere værdierne i stigende rækkefølge, og samler dem derefter i en List<Integer>.

Resultatet er en sorteret liste af unikke, afrundede priser, der kan anvendes til yderligere beregninger eller visningsformål.

1. Hvad gør metoden collect() i Stream API?

2. Hvilken ekstra funktionalitet giver Collector-interfacet sammenlignet med collect() med funktionelle interfaces?

question mark

Hvad gør metoden collect() i Stream API?

Select the correct answer

question mark

Hvilken ekstra funktionalitet giver Collector-interfacet sammenlignet med collect() med funktionelle interfaces?

Select the correct answer

Var alt klart?

Hvordan kan vi forbedre det?

Tak for dine kommentarer!

Sektion 3. Kapitel 1

Spørg AI

expand

Spørg AI

ChatGPT

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

bookCollect() Samler Streamelementer i en Samling

Stryg for at vise menuen

Du er allerede bekendt med terminale operationer og har endda brugt dem i tidligere eksempler og øvelser. Nu er det tid til at se nærmere på, hvordan de fungerer. Først ser vi på collect()-metoden, som er en af de vigtigste terminale operationer i Stream API.

Metoden collect()

Det er et af de mest kraftfulde værktøjer ved arbejde med streams, da det gør det muligt at akkumulere resultater i en List, Set eller Map, samt udføre komplekse grupperinger og statistiske beregninger.

Der findes to implementeringer af collect()-metoden—lad os undersøge begge.

Brug af collect() med funktionelle grænseflader

Metoden collect() i Stream API kan anvendes med tre funktionelle grænseflader for at give fuld kontrol over datainnsamling:

  • Supplier<R> supplier – opretter en tom samling (R), hvor elementer vil blive gemt. For eksempel initialiserer ArrayList::new en ny liste;
  • BiConsumer<R, ? super T> accumulator – tilføjer stream-elementer (T) til samlingen (R). For eksempel tilføjer List::add elementer til en liste;
  • BiConsumer<R, R> combiner – sammenfletter to samlinger ved parallel behandling. For eksempel kombinerer List::addAll lister til én.

Alle tre komponenter arbejder sammen for at give fleksibilitet i datainnsamling. Først opretter supplier en tom samling, der bruges til at akkumulere elementer fra streamen. Derefter tilføjer accumulator hvert element, efterhånden som streamen behandles. Dette forløb forbliver enkelt i en sekventiel stream.

Dog bliver det mere komplekst, når der arbejdes med parallelle streams (parallelStream()).

Databehandling er fordelt på flere tråde, hvor hver tråd opretter sin egen separate samling. Når behandlingen er færdig, skal disse individuelle samlinger flettes til et enkelt resultat. Her kommer combiner ind i billedet og kombinerer effektivt de separate dele til én samlet samling.

Praktisk eksempel

Du arbejder for en onlinebutik og har en liste over produkter. Din opgave er at indsamle kun de produkter, der koster mere end $500, ved hjælp af collect()-metoden med tre parametre.

Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
package com.example; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { // Initial list of products List<Product> productList = List.of( new Product("Laptop", 1200.99), new Product("Phone", 599.49), new Product("Headphones", 199.99), new Product("Monitor", 299.99), new Product("Tablet", 699.99) ); // Filtering and collecting products over $500 using `collect()` List<Product> expensiveProducts = productList.parallelStream() .filter(product -> product.getPrice() > 500) // Keep only expensive products .collect( ArrayList::new, // Create a new list (list, product) -> list.add(product), // Add each product to the list ArrayList::addAll // Merge lists (if the stream is parallel) ); // Print the result System.out.print("Products over $500: " + expensiveProducts); } } class Product { private String name; private double price; Product(String name, double price) { this.name = name; this.price = price; } public String getName() { return name; } public double getPrice() { return price; } @Override public String toString() { return name + " ($" + price + ")"; } }

Metoden collect() tager tre argumenter, som hver definerer et trin i indsamlingen af elementer til en liste:

  • ArrayList::new (Supplier) → opretter en tom ArrayList<Product> til at gemme resultaterne;

  • (list, product) -> list.add(product) (BiConsumer) → tilføjer hver Product til listen, hvis den opfylder filterbetingelsen (price > 500);

  • ArrayList::addAll (BiConsumer) → samler flere lister ved brug af parallelle streams og sikrer, at alle filtrerede produkter kombineres i en enkelt liste.

Selvom den tredje parameter primært bruges til parallel behandling, er den påkrævet af collect().

Brug af collect() med Collector-interfacet

Ud over at arbejde med tre funktionelle interfaces kan metoden collect() i Stream API også bruges med foruddefinerede implementeringer af Collector-interfacet.

Denne tilgang er mere fleksibel og bekvem, da den tilbyder indbyggede metoder til at arbejde med samlinger.

Collector<T, A, R>-interfacet består af flere centrale metoder:

  • Supplier<A> supplier() – opretter en tom beholder til at akkumulere elementer;
  • BiConsumer<A, T> accumulator() – definerer, hvordan elementer tilføjes til beholderen;
  • BinaryOperator<A> combiner() – samler to beholdere ved parallel behandling;
  • Function<A, R> finisher() – omdanner beholderen til det endelige resultat.

Som du kan se, ligner denne struktur den collect()-metode, der arbejder med funktionelle grænseflader, men den introducerer finisher()-metoden. Dette ekstra trin muliggør yderligere behandling af de indsamlede data, før det endelige resultat returneres—f.eks. sortering af listen inden returnering.

Derudover tilbyder Collector-grænsefladen metoden characteristics(), som definerer egenskaber, der hjælper med at optimere stream-udførelse:

Disse egenskaber hjælper Stream API med at optimere ydelsen. For eksempel, hvis en samling er iboende uordnet, kan angivelse af UNORDERED forhindre unødvendig sortering, hvilket gør operationen mere effektiv.

Praktisk eksempel

Forestil dig, at du driver en onlinebutik og skal behandle produktpriser inden indsamling. For eksempel ønsker du at afrunde hver pris til nærmeste hele tal, fjerne dubletter og sortere den endelige liste.

Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
package com.example; import java.util.*; import java.util.function.*; import java.util.stream.Collector; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<Double> prices = List.of(1200.99, 599.49, 199.99, 599.49, 1200.49, 200.0); // Using a custom `Collector` to round prices, remove duplicates, and sort List<Integer> processedPrices = prices.parallelStream() .collect(new RoundedSortedCollector()); System.out.println("Processed product prices: " + processedPrices); } } // Custom `Collector` that rounds prices, removes duplicates, and sorts them class RoundedSortedCollector implements Collector<Double, Set<Integer>, List<Integer>> { @Override public Supplier<Set<Integer>> supplier() { // Creates a `HashSet` to store unique rounded values return HashSet::new; } @Override public BiConsumer<Set<Integer>, Double> accumulator() { // Rounds price and adds to the set return (set, price) -> set.add((int) Math.round(price)); } @Override public BinaryOperator<Set<Integer>> combiner() { return (set1, set2) -> { set1.addAll(set2); // Merges two sets return set1; }; } @Override public Function<Set<Integer>, List<Integer>> finisher() { return set -> set.stream() .sorted() // Sorts the final list .toList(); } @Override public Set<Characteristics> characteristics() { // Order is not important during accumulation return Set.of(Characteristics.UNORDERED); } }

Databehandlingen indledes ved at sende dataene ind i en brugerdefineret Collector kaldet RoundedSortedCollector.

Denne collector akkumulerer først alle priser i et Set<Integer>, hvilket sikrer, at dubletter automatisk fjernes. Før hver værdi tilføjes, afrundes prisen ved hjælp af Math.round(price) og konverteres til en int. For eksempel vil både 1200.99 og 1200.49 blive til 1200, mens 199.99 afrundes til 200.

Hvis strømmen kører i paralleltilstand, sammenfletter combiner()-metoden to sæt ved at tilføje alle elementer fra det ene sæt til det andet. Dette trin er afgørende i multitrådede miljøer.

I den afsluttende fase, efter at alle priser er indsamlet, omdanner finisher()-metoden sættet til en sorteret liste. Den konverterer Set<Integer> til en stream, anvender sorted() for at arrangere værdierne i stigende rækkefølge, og samler dem derefter i en List<Integer>.

Resultatet er en sorteret liste af unikke, afrundede priser, der kan anvendes til yderligere beregninger eller visningsformål.

1. Hvad gør metoden collect() i Stream API?

2. Hvilken ekstra funktionalitet giver Collector-interfacet sammenlignet med collect() med funktionelle interfaces?

question mark

Hvad gør metoden collect() i Stream API?

Select the correct answer

question mark

Hvilken ekstra funktionalitet giver Collector-interfacet sammenlignet med collect() med funktionelle interfaces?

Select the correct answer

Var alt klart?

Hvordan kan vi forbedre det?

Tak for dine kommentarer!

Sektion 3. Kapitel 1
some-alt