Course Content
Stream API
Stream API
Collectors Utility Class for Stream API
The collect()
method in Stream API is already familiar to us as a powerful tool for gathering stream elements into convenient data structures. However, collect()
itself requires a Collector
implementation, which can make the process more complex.
This is where the Collectors
class comes in handy, providing a set of pre-built implementations for the most common operations. It simplifies tasks such as collecting data into collections, grouping, and counting, making these operations much more straightforward.
Key Methods of the Collectors Class
The Collectors
class offers numerous ready-made solutions, eliminating the need for manually implementing a Collector
. Here are some of the main methods:
The Stream API also offers the built-in toList()
method, which you have used before. This method has a concise syntax, making it our preferred choice. However, you are free to use any implementation that best fits your needs.
Converting a List to a Map
Suppose you have a list of products with their prices, stored as strings in the format Name=Price
. Our goal is to create a Map
where the key is the product name and the value is its price.
Main
package com.example; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { List<String> productData = List.of("Laptop:1500", "Smartphone:900", "Monitor:1200"); // Convert the list of strings into a `Map` by splitting the string at "=" Map<String, Integer> productPriceMap = productData.stream() .collect(Collectors.toMap( item -> item.split(":")[0], // Key - product name item -> Integer.parseInt(item.split(":")[1]) // Value - price )); System.out.println("Product Map: " + productPriceMap); } }
Here, you use toMap()
to split each string (split("=")
) and create a Map<String, Integer>
, where the key is the product name and the value is its price as an integer. For example, the string Laptop=1500
transforms into an entry Laptop -> 1500
.
Grouping Products by First Letter
Let's group products by their first letter to see which items start with the same letter.
Main
package com.example; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { List<String> products = List.of( "Laptop", "Lamp", "Laser Printer", "Desktop PC", "Drone", "Smartphone", "Smartwatch", "Monitor", "Mouse" ); // Grouping products by the first letter of their name Map<Character, List<String>> groupedProducts = products.stream() .collect(Collectors.groupingBy(product -> product.charAt(0))); System.out.println("Products grouped by first letter: " + groupedProducts); } }
The program creates a List
of product names and groups them by their first letter using groupingBy()
. The result is stored in a Map
, where the key is charAt(0)
, and the value is a list of matching products. Finally, the grouped products are printed.
Splitting Prices
Collectors.partitioningBy
is a special collector in the Stream API that splits elements into two groups based on a given predicate.
It returns a Map<Boolean, List<T>>
, where true
represents elements that match the condition, and false
represents those that don't.
This is useful for separating data, such as filtering even and odd numbers or high and low-priced products.
Example
Let's divide product prices into two categories: expensive (greater than $1000) and cheap ($1000 or less).
Main
package com.example; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { List<Integer> prices = List.of(1500, 900, 1200, 1100, 300); // Partition prices into expensive and cheap categories Map<Boolean, List<Integer>> partitionedPrices = prices.stream() .collect(Collectors.partitioningBy(price -> price > 1000)); System.out.println("Expensive products: " + partitionedPrices.get(true)); System.out.print("Cheap products: " + partitionedPrices.get(false)); } }
The partitioningBy()
method splits the prices list into two groups. If a price is greater than 1000, it is placed under the key true
; otherwise, it goes under false
.
Counting Products
Let's count how many products start with the same letter.
Main
package com.example; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { List<String> products = List.of( "Laptop", "Lamp", "Laser Printer", "Desktop PC", "Dishwasher", "Drone", "Smartphone", "Smartwatch", "Speaker", "Monitor", "Mouse", "Microphone" ); // Count how many products start with each letter Map<Character, Long> countByLetter = products.stream() .collect(Collectors.groupingBy(product -> product.charAt(0), Collectors.counting())); System.out.println("Product count by first letter: " + countByLetter); } }
The program processes the List
of products using stream()
, then applies groupingBy()
with charAt(0)
to group words by their first letter. The counting()
collector counts how many products fall into each group, and the final Map
stores the letter as a key and the count as a value.
Joining Product Names
Let's concatenate product names into a single string, separating them with commas.
Main
package com.example; import java.util.List; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { List<String> products = List.of("Laptop", "Smartphone", "Monitor"); // Join product names into a single comma-separated string String productNames = products.stream() .collect(Collectors.joining(", ")); System.out.println("Product list: " + productNames); } }
The Collectors.joining(", ")
method concatenates all product names into a single string, separating them with commas. Finally, the resulting string is printed to display the product list in a readable format.
1. What does the following code do?
2. Which Collectors
method should be used to group products by their first letter?
Thanks for your feedback!