![]() | |
|
When you are done with a stream, you often just want to look at the results instead of reducing them to a value.
You can use Stream.collect(...)
method. It takes three arguments:
A supplier to make new instances of the target object, for example, a constructor for a hash set
An accumulator that adds an element to the target, for example, an add
method
An combiner that merges two objects into one, such as addAll
There is a convenient Collector
interface for these three functions, and a Collectors
class with factory methods for common collectors. To collect a stream into a list or set, you can simply call:
List<String> result = stream.collect(Collectors.toList());
or
Set<String> result = stream.collect(Collectors.toSet());
or if you want to control which kind of set you get, use the following call instead:
TreeSet<String> result = stream.collect(Collectors.toCollection(TreeSet::new));
![]() | |
|
Collectors.averagingDouble(ToDoubleFunction<? super T> mapper)
Collectors.averagingDouble
calculates the average of stream element as double
data type.
It returns a Collector
instance. The collect
method of stream can accept the
Collector
instance and will return the average value calculated by
Collectors.averagingDouble
.
List<String> list = Arrays.asList("A", "BB", "CCC", "DDDD", "CCCCC"); double result = list.stream().collect(Collectors.averagingDouble(s -> s.length())); System.out.println(result);
output:
3.0
Collectors.groupingBy(Function<? super T,? extends K> classifier)
A "group by" operation on input elements of type T
, grouping elements according to a
classification function, and returning the results in a map.
public class Employee { private String name; private String department; public Employee(String n, String d) { name = n; department = d; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDepartment() { return department; } public void setDepartment(String department) { this.department = department; } @Override public String toString() { return getName() + " from " + getDepartment() + " dept"; } }
A common database operation is to group items in a set, based on one or more properties. It can
be easily translated in a single, very readable statement by rewriting it in a more functional style
as encouraged by Java 8.0. Suppose you want to group employees in a company based on
their department. You can easily perform this task using a collector returned by the
Collectors.groupingBy
factory method as follows:
Employee e1 = new Employee("Mikalai", "Development"); Employee e2 = new Employee("Volha", "HR"); Employee e3 = new Employee("Anastasia", "Management"); Employee e4 = new Employee("Daria", "Management"); Employee e5 = new Employee("Ivan", "Management"); Stream<Employee> str = Stream.of(e1, e2, e3, e4, e5); // Group Employees by department Map<String, List<Employee>> map = str.collect(Collectors.groupingBy(Employee::getDepartment)); System.out.println(map.get("Management"));
Here, we passing to the groupingBy
method a Function
(expressed in the form
of a method reference) extracting the corresponding department for each Employee
in the
stream. We call this Function
a classification function because it is used to classify
the elements of the stream into different groups. The result of this grouping operation is a
java.util.Map
having as map key the value returned by the classification function and as
corresponding map value a list of all the items in the stream having that classified value:
[Anastasia from Management dept, Daria from Management dept, Ivan from Management dept]
It is possible to pass a second collector to the groupingBy
method to achieve a
multilevel grouping. For instance, it is possible to count the number of employees in
each department, by passing the Collectors.counting()
collector as a second
argument to the groupingBy
collector:
// Counting Employees by department Map<String, Long> map = str.collect(Collectors.groupingBy(Employee::getDepartment, Collectors.counting())); System.out.println(map.get("Management"));
output:
3
Collectors.joining()
The collector returned by the Collectors.joining()
factory method concatenates into a
single string all strings resulting from invoking the toString()
method on each object
in the stream. This means you can concatenate the names of all the employees in the company as
follows:
String names = str.map(Employee::getName).collect(Collectors.joining());
Note that joining()
internally makes use of a StringBuilder
to append
the generated strings into one. It produces the following string:
MikalaiVolhaAnastasiaDariaIvan
which is not very readable.
Fortunately, the joining factory method has an overloaded version that accepts a delimiter string between two consecutive elements, so you can obtain a comma-separated list of the employees' names with:
String names = str.map(Employee::getName).collect(Collectors.joining(", "));
It produces the following string:
Mikalai, Volha, Anastasia, Daria, Ivan
![]() | |
|
Collectors.partitioningBy(Predicate<? super T> predicate)
Partitions the input elements according to a Predicate
.
When the classifier function is a Predicate
function (that is, a function returning
a boolean
value), the stream elements are partitioned into two lists: those where
the function returns true
and false
. In this case, it is more efficient to
use partitioningBy
instead of groupingBy
. For example, here we split
all employees into those that are managers, and all others:
Employee e1 = new Employee("Mikalai", "Development"); Employee e2 = new Employee("Volha", "HR"); Employee e3 = new Employee("Anastasia", "Management"); Employee e4 = new Employee("Daria", "Management"); Employee e5 = new Employee("Ivan", "Management"); Stream<Employee> str = Stream.of(e1, e2, e3, e4, e5); // Parition Employees to managers and others Map<Boolean, List<Employee>> map = str.collect(Collectors.partitioningBy(e -> "Management".equals(e.getDepartment()))); System.out.println(map.get(Boolean.TRUE));
resulting in:
[Anastasia from Management dept, Daria from Management dept, Ivan from Management dept]
You can think of partitioning as a special case of grouping. Partitioning may only use Boolean
as Map
key, so you can have maximum two entries (each entry represented by a list of
stream elements) in the resulting map.
![]() ![]() ![]() |