Published: July 30, 2016
Introduction
The Collectors utility class in Java 8 makes it easy to collect elements from a stream into various data structures, such as lists, sets, or maps. Collectors also provide advanced aggregation operations, like grouping, partitioning, and reducing data.
In this tutorial, we’ll explore the power of the Collectors class and how to use it for advanced stream operations. You will learn about:
- Commonly used collector methods.
- How to collect data into different collections.
- How to group and partition data using collectors.
By the end of this tutorial, you’ll be able to use Collectors to simplify and optimize data collection tasks in your applications.
Common Collectors Methods
The Collectors class provides several predefined methods to collect data in useful ways. Some of the most common collectors are:
- toList(): Collects elements into a
List
. - toSet(): Collects elements into a
Set
. - toMap(): Collects elements into a
Map
. - joining(): Joins elements into a
String
. - groupingBy(): Groups elements by a property.
- partitioningBy(): Partitions elements into two groups based on a predicate.
Let’s go over some of these methods and see how to use them in stream operations.
Collecting Data into a List
The most basic collection operation is converting a stream into a list. You can use the Collectors.toList()
method to collect all elements from a stream into a List
.
Example 1: Collecting Data into a List
import java.util.List;
import java.util.Arrays;
import java.util.stream.Collectors;
public class StreamExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("John", "Jane", "Jack", "Jill", "James");
// Collect names into a list
List<String> nameList = names.stream()
.filter(name -> name.startsWith("J"))
.collect(Collectors.toList());
System.out.println(nameList); // Output: [John, Jane, Jack, Jill, James]
}
}
In this example, we filter the names starting with “J” and collect the results into a list.
Collecting Data into a Set
Sometimes, you may want to collect elements into a Set
to ensure uniqueness. You can use the Collectors.toSet()
method for this.
Example 2: Collecting Data into a Set
import java.util.Set;
import java.util.Arrays;
import java.util.stream.Collectors;
public class StreamExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("John", "Jane", "Jack", "Jill", "James", "John");
// Collect names into a set (duplicates will be removed)
Set<String> nameSet = names.stream()
.filter(name -> name.startsWith("J"))
.collect(Collectors.toSet());
System.out.println(nameSet); // Output: [John, Jane, Jack, Jill, James]
}
}
Notice that the Set
automatically removes any duplicate values (like “John” appearing twice in the original list).
Collecting Data into a Map
You can also collect elements into a Map
using the Collectors.toMap()
method. This is particularly useful when you want to map an object’s properties to keys and values.
Example 3: Collecting Data into a Map
import java.util.Map;
import java.util.List;
import java.util.Arrays;
import java.util.stream.Collectors;
public class StreamExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("John", "Jane", "Jack", "Jill");
// Collect names into a map where the name is the key and the length of the name is the value
Map<String, Integer> nameLengthMap = names.stream()
.collect(Collectors.toMap(name -> name, String::length));
System.out.println(nameLengthMap); // Output: {John=4, Jane=4, Jack=4, Jill=4}
}
}
In this example, the toMap()
method collects the names as keys and their corresponding lengths as values.
Joining Elements into a String
If you want to join elements into a single String
, you can use the Collectors.joining()
method. This is very useful for tasks such as joining words into a sentence.
Example 4: Joining Elements into a String
import java.util.List;
import java.util.Arrays;
import java.util.stream.Collectors;
public class StreamExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("John", "Jane", "Jack", "Jill");
// Join names into a single string with commas separating them
String nameString = names.stream()
.collect(Collectors.joining(", "));
System.out.println(nameString); // Output: John, Jane, Jack, Jill
}
}
You can also specify a delimiter, a prefix, and a suffix to customize the result further.
Grouping Elements by a Property
The Collectors.groupingBy()
method allows you to group elements by a certain property, such as a category or a value. This is similar to SQL’s GROUP BY
clause.
Example 5: Grouping Elements by Length
import java.util.List;
import java.util.Map;
import java.util.Arrays;
import java.util.stream.Collectors;
public class StreamExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("John", "Jane", "Jack", "Jill");
// Group names by their length
Map<Integer, List<String>> groupedByLength = names.stream()
.collect(Collectors.groupingBy(String::length));
System.out.println(groupedByLength); // Output: {4=[John, Jane, Jack, Jill]}
}
}
Here, we group the names by their length. The resulting map has the length as the key and a list of names with that length as the value.
Partitioning Elements into Two Groups
The Collectors.partitioningBy()
method divides elements into two groups based on a predicate. The result is a map with Boolean
keys: one for true
and one for false
.
Example 6: Partitioning Elements by Length
import java.util.List;
import java.util.Map;
import java.util.Arrays;
import java.util.stream.Collectors;
public class StreamExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("John", "Jane", "Jack", "Jill");
// Partition names into two groups: those with more than 4 letters and those with 4 or fewer letters
Map<Boolean, List<String>> partitioned = names.stream()
.collect(Collectors.partitioningBy(name -> name.length() > 4));
System.out.println(partitioned); // Output: {false=[John, Jane, Jack], true=[Jill]}
}
}
In this case, the names are partitioned into two lists: one where the name length is greater than 4, and the other where it is less than or equal to 4.
Conclusion
The Collectors utility class provides a variety of powerful tools for collecting and grouping data in streams. By using toList(), toSet(), groupingBy(), joining(), and other collectors, you can efficiently aggregate, filter, and manipulate data.
In the next tutorial, we’ll dive deeper into parallel streams and how they can be used to improve performance when processing large datasets.