Collect() Samlar Strömelement i en Samling
Du är redan bekant med terminaloperationer och har till och med använt dem i tidigare exempel och övningar. Nu är det dags att titta närmare på hur de fungerar. Först ut är metoden collect(), som är en av de viktigaste terminaloperationerna i Stream API.
Metoden collect()
Det är ett av de mest kraftfulla verktygen vid arbete med streams, vilket gör det möjligt att samla resultat i en List, Set eller Map, samt utföra komplexa grupperingar och statistiska beräkningar.
Det finns två implementationer av metoden collect()—låt oss utforska båda.
Användning av collect() med funktionella gränssnitt
Metoden collect() i Stream API kan användas med tre funktionella gränssnitt för att ge full kontroll över datainsamling:
Supplier<R> supplier– skapar en tom samling (R) där element kommer att lagras. Till exempel initierarArrayList::newen ny lista;BiConsumer<R, ? super T> accumulator– lägger till strömelement (T) i samlingen (R). Exempelvis läggerList::addtill objekt i en lista;BiConsumer<R, R> combiner– slår ihop två samlingar vid parallell bearbetning. Till exempel kombinerarList::addAlllistor till en.
Alla tre komponenter samverkar för att ge flexibilitet vid datainsamling. Först skapar supplier en tom samling som används för att ackumulera element från strömmen. Därefter lägger accumulator till varje element när strömmen bearbetas. Detta förlopp är enkelt i en sekventiell ström.
Vid arbete med parallella strömmar (parallelStream()) blir det dock mer komplext.
Databehandlingen delas upp mellan flera trådar, där varje tråd skapar sin egen separata samling. När bearbetningen är klar måste dessa individuella samlingar slås samman till ett enda resultat. Här används combiner, som effektivt kombinerar de separata delarna till en enhetlig samling.
Praktiskt exempel
Du arbetar för en webbutik och har en produktlista. Din uppgift är att samla endast de produkter som kostar mer än $500 med hjälp av metoden collect() med tre parametrar.
Main.java
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152package 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() tar tre argument, där varje argument definierar ett steg i insamlingen av element till en lista:
-
ArrayList::new(Supplier) → skapar en tomArrayList<Product>för att lagra resultaten; -
(list, product) -> list.add(product)(BiConsumer) → lägger till varjeProducti listan om den uppfyller filtervillkoret (price > 500); -
ArrayList::addAll(BiConsumer) → slår samman flera listor vid användning av parallella strömmar, vilket säkerställer att alla filtrerade produkter kombineras till en enda lista.
Även om den tredje parametern främst används för parallell bearbetning, krävs den av collect().
Användning av collect() med Collector-gränssnittet
Förutom att arbeta med tre funktionella gränssnitt kan metoden collect() i Stream API även användas med fördefinierade implementationer av Collector-gränssnittet.
Detta tillvägagångssätt är mer flexibelt och bekvämt eftersom det tillhandahåller inbyggda metoder för att arbeta med samlingar.
Collector<T, A, R>-gränssnittet består av flera viktiga metoder:
Supplier<A> supplier()– skapar en tom behållare för att ackumulera element;BiConsumer<A, T> accumulator()– definierar hur element läggs till i behållaren;BinaryOperator<A> combiner()– slår samman två behållare vid parallell bearbetning;Function<A, R> finisher()– omvandlar behållaren till slutresultatet.
Som du kan se är denna struktur liknande collect()-metoden som arbetar med funktionella gränssnitt, men den introducerar metoden finisher(). Detta extra steg möjliggör ytterligare bearbetning av de insamlade uppgifterna innan det slutliga resultatet returneras—till exempel att sortera listan innan den returneras.
Dessutom tillhandahåller Collector-gränssnittet metoden characteristics(), som definierar egenskaper som hjälper till att optimera strömexekveringen:
Dessa egenskaper hjälper Stream API att optimera prestanda. Till exempel, om en samling är oordnad från början, kan specificeringen av UNORDERED förhindra onödig sortering och göra operationen mer effektiv.
Praktiskt exempel
Föreställ dig att du driver en webbutik och behöver bearbeta produktpriser innan de samlas in. Till exempel vill du avrunda varje pris till närmaste heltal, ta bort dubbletter och sortera den slutliga listan.
Main.java
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455package 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); } }
Bearbetningen av data påbörjas genom att skicka in dem i en anpassad Collector kallad RoundedSortedCollector.
Denna collector samlar först alla priser i en Set<Integer>, vilket säkerställer att dubbletter automatiskt tas bort. Innan varje värde läggs till avrundas priset med Math.round(price) och konverteras till en int. Till exempel kommer både 1200.99 och 1200.49 att bli 1200, medan 199.99 avrundas uppåt till 200.
Om strömmen körs i parallellt läge slår metoden combiner() samman två mängder genom att lägga till alla element från en mängd till en annan. Detta steg är avgörande för flertrådade miljöer.
I det sista steget, efter att alla priser har samlats in, omvandlar metoden finisher() mängden till en sorterad lista. Den konverterar Set<Integer> till en ström, tillämpar sorted() för att ordna värdena i stigande ordning och samlar dem sedan i en List<Integer>.
Resultatet blir en sorterad lista med unika, avrundade priser som kan användas för vidare beräkningar eller visningsändamål.
1. Vad gör metoden collect() i Stream API?
2. Vilken ytterligare funktionalitet erbjuder gränssnittet Collector jämfört med collect() med funktionella gränssnitt?
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
Collect() Samlar Strömelement i en Samling
Svep för att visa menyn
Du är redan bekant med terminaloperationer och har till och med använt dem i tidigare exempel och övningar. Nu är det dags att titta närmare på hur de fungerar. Först ut är metoden collect(), som är en av de viktigaste terminaloperationerna i Stream API.
Metoden collect()
Det är ett av de mest kraftfulla verktygen vid arbete med streams, vilket gör det möjligt att samla resultat i en List, Set eller Map, samt utföra komplexa grupperingar och statistiska beräkningar.
Det finns två implementationer av metoden collect()—låt oss utforska båda.
Användning av collect() med funktionella gränssnitt
Metoden collect() i Stream API kan användas med tre funktionella gränssnitt för att ge full kontroll över datainsamling:
Supplier<R> supplier– skapar en tom samling (R) där element kommer att lagras. Till exempel initierarArrayList::newen ny lista;BiConsumer<R, ? super T> accumulator– lägger till strömelement (T) i samlingen (R). Exempelvis läggerList::addtill objekt i en lista;BiConsumer<R, R> combiner– slår ihop två samlingar vid parallell bearbetning. Till exempel kombinerarList::addAlllistor till en.
Alla tre komponenter samverkar för att ge flexibilitet vid datainsamling. Först skapar supplier en tom samling som används för att ackumulera element från strömmen. Därefter lägger accumulator till varje element när strömmen bearbetas. Detta förlopp är enkelt i en sekventiell ström.
Vid arbete med parallella strömmar (parallelStream()) blir det dock mer komplext.
Databehandlingen delas upp mellan flera trådar, där varje tråd skapar sin egen separata samling. När bearbetningen är klar måste dessa individuella samlingar slås samman till ett enda resultat. Här används combiner, som effektivt kombinerar de separata delarna till en enhetlig samling.
Praktiskt exempel
Du arbetar för en webbutik och har en produktlista. Din uppgift är att samla endast de produkter som kostar mer än $500 med hjälp av metoden collect() med tre parametrar.
Main.java
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152package 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() tar tre argument, där varje argument definierar ett steg i insamlingen av element till en lista:
-
ArrayList::new(Supplier) → skapar en tomArrayList<Product>för att lagra resultaten; -
(list, product) -> list.add(product)(BiConsumer) → lägger till varjeProducti listan om den uppfyller filtervillkoret (price > 500); -
ArrayList::addAll(BiConsumer) → slår samman flera listor vid användning av parallella strömmar, vilket säkerställer att alla filtrerade produkter kombineras till en enda lista.
Även om den tredje parametern främst används för parallell bearbetning, krävs den av collect().
Användning av collect() med Collector-gränssnittet
Förutom att arbeta med tre funktionella gränssnitt kan metoden collect() i Stream API även användas med fördefinierade implementationer av Collector-gränssnittet.
Detta tillvägagångssätt är mer flexibelt och bekvämt eftersom det tillhandahåller inbyggda metoder för att arbeta med samlingar.
Collector<T, A, R>-gränssnittet består av flera viktiga metoder:
Supplier<A> supplier()– skapar en tom behållare för att ackumulera element;BiConsumer<A, T> accumulator()– definierar hur element läggs till i behållaren;BinaryOperator<A> combiner()– slår samman två behållare vid parallell bearbetning;Function<A, R> finisher()– omvandlar behållaren till slutresultatet.
Som du kan se är denna struktur liknande collect()-metoden som arbetar med funktionella gränssnitt, men den introducerar metoden finisher(). Detta extra steg möjliggör ytterligare bearbetning av de insamlade uppgifterna innan det slutliga resultatet returneras—till exempel att sortera listan innan den returneras.
Dessutom tillhandahåller Collector-gränssnittet metoden characteristics(), som definierar egenskaper som hjälper till att optimera strömexekveringen:
Dessa egenskaper hjälper Stream API att optimera prestanda. Till exempel, om en samling är oordnad från början, kan specificeringen av UNORDERED förhindra onödig sortering och göra operationen mer effektiv.
Praktiskt exempel
Föreställ dig att du driver en webbutik och behöver bearbeta produktpriser innan de samlas in. Till exempel vill du avrunda varje pris till närmaste heltal, ta bort dubbletter och sortera den slutliga listan.
Main.java
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455package 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); } }
Bearbetningen av data påbörjas genom att skicka in dem i en anpassad Collector kallad RoundedSortedCollector.
Denna collector samlar först alla priser i en Set<Integer>, vilket säkerställer att dubbletter automatiskt tas bort. Innan varje värde läggs till avrundas priset med Math.round(price) och konverteras till en int. Till exempel kommer både 1200.99 och 1200.49 att bli 1200, medan 199.99 avrundas uppåt till 200.
Om strömmen körs i parallellt läge slår metoden combiner() samman två mängder genom att lägga till alla element från en mängd till en annan. Detta steg är avgörande för flertrådade miljöer.
I det sista steget, efter att alla priser har samlats in, omvandlar metoden finisher() mängden till en sorterad lista. Den konverterar Set<Integer> till en ström, tillämpar sorted() för att ordna värdena i stigande ordning och samlar dem sedan i en List<Integer>.
Resultatet blir en sorterad lista med unika, avrundade priser som kan användas för vidare beräkningar eller visningsändamål.
1. Vad gör metoden collect() i Stream API?
2. Vilken ytterligare funktionalitet erbjuder gränssnittet Collector jämfört med collect() med funktionella gränssnitt?
Tack för dina kommentarer!