Java 8 Stream.collect() – Stream.Collectors APIs Examples

Java 8 Stream.collect() - Stream.Collectors APIs with Examples

Java 8 Stream.collect() – Stream.Collectors APIs Examples

[no_toc]
Java 8 provides an extremely powerful abstract concept Stream with many useful mechanics for consuming and processing data in Java Collection. In the tutorial, We will do lots of examples to explore more the helpful of Stream API with Collectors operation on the specific topic: “Java 8 Stream Collector”.

What will we do?

  • Explore Stream Collect Method signatures
  • Apply Stream Collectors Util with Examples

Now let’s do more details!

Related posts:
Java 8 Stream Reduce Examples
Java 8 Stream FlatMap Examples

Java Stream.collector()

Stream.collect(supplier, accumulator, combiner)

– Method Signature:

<R> R collect(Supplier<R> supplier,
			  BiConsumer<R, ? super T> accumulator,
			  BiConsumer<R, R> combiner);

It performs a mutable reduction operation on the elements of this stream to update the result container.

R is the type of result container
supplier is a function that creates a new mutable result container.
combiner is an associative, non-interfering, stateless function that accepts two partial result containers and merges them.

How does it work?

R result = supplier.get();
     for (T element : this stream)
         accumulator.accept(result, element);
     return result;

– Example:

class Averager implements IntConsumer{
    private int total = 0;
    private int count = 0;
        
    public double average() {
        return count > 0 ? ((double) total)/count : 0;
    }
        
    public void accept(int i) { total += i; count++; }
    
    public void combine(Averager other) {
        total += other.total;
        count += other.count;
    }
    
    public String toString() {
    	return String.format("Total Salaries = %d, Number Developers = %d, Average Salary = %.2f", this.total, this.count, average());
    }
}
		
List developers= Arrays.asList(new Developer("Jack", 5400),
		new Developer("Joe", 7500),
		new Developer("Jane", 6500),
		new Developer("Mary", 6200)
	);

Averager averageCollect = developers.stream()
		.map(Developer::getSalary)
		.collect(Averager::new, Averager::accept, Averager::combine);

System.out.println(averageCollect);
// Total Salaries = 25600, Number Developers = 4, Average Salary = 6400,00

Stream.collect(Collector collector)

– Method Signature:

<R, A> R collect(Collector<? super T, A, R> collector);

-> A Collector encapsulates the functions used as arguments to collect(Supplier, BiConsumer, BiConsumer).

R: is the type of the result
A: is the intermediate accumulation type of the Collector

Example:

List intLst = Arrays.asList(2, 14, 4, 17, 5, 6, 1, 8, 2, 7, 8);

Integer total = intLst.stream().collect(Collectors.summingInt(i->i));
System.out.println(total);
// 74

java.util.stream.Collectors Examples

java.util.stream.Collectors is an implementations of Collector that implement various useful reduction operations, such as accumulating elements into collections, summarizing elements according to various criteria.

Collectors.toList(), toSet(), toMap()

Collectors.toList()

– Returns a Collector that accumulates the input elements into a new List.

List intLst = Arrays.asList(1, 2, 3, 4, 2, 5, 6, 4, 8, 2, 7, 8);

List evenLst = intLst.stream().filter(i->i%2==0).collect(Collectors.toList());
System.out.println(evenLst);
// [2, 4, 2, 6, 4, 8, 2, 8]

Collectors.toSet()

– Returns a Collector that accumulates the input elements into a new Set.

List<Integer> intLst = Arrays.asList(1, 2, 3, 4, 2, 5, 6, 4, 8, 2, 7, 8);

Set<Integer> intSet = intLst.stream().collect(Collectors.toSet());
System.out.println(intSet);
// [1, 2, 3, 4, 5, 6, 7, 8]

Collectors.toMap()

– Returns a Collector that accumulates elements into a Map whose keys and values are the result of applying the provided mapping functions to the input elements.

class Developer{
	private String name;
	private Integer salary;
	
	Developer(String name, Integer salary){
		this.name = name;
		this.salary = salary;
	}
	
	public String getName() {
		return this.name;
	}
	
	public Integer getSalary() {
		return this.salary;
	}
}

List developers= Arrays.asList(new Developer("Jack", 5400),
											new Developer("Joe", 7500),
											new Developer("Jane", 6500),
											new Developer("Mary", 6200)
										);

Map results = developers.stream().collect(Collectors.toMap(Developer::getName, Developer::getSalary));
System.out.println(results);
// {Joe=7500, Jack=5400, Jane=6500, Mary=6200}

Collectors.toCollection()

With Collectors.toCollection(), we can custom the implementation of collection.

public static <T, C extends Collection<T>>
    Collector<T, ?, C> toCollection(Supplier<C> collectionFactory) 

-> It returns a Collector that accumulates the input elements into a new Collection.
The Collection is created by the provided factory.

– Example:

List<Integer> linkedList = intLst.stream().collect(Collectors.toCollection(LinkedList::new));

Collectors.joining()

It returns a Collector that concatenates the input elements into a String, in encounter order

– Signature 1:

public static Collector<CharSequence, ?, String> joining()

Example:

List strLst = Arrays.asList("https://", "gro", "konez", ".com");

String result = strLst.stream().collect(Collectors.joining());
System.out.println(result);
// https://ozenero.com

– Signature 2:

public static Collector joining(CharSequence delimiter)

-> It returns a Collector that concatenates the input elements, separated by the specified delimiter, in encounter order.

Example:

List strLst = Arrays.asList("Jack", "Joe", "Jane", "Mary");

String result = strLst.stream().collect(Collectors.joining(", "));
System.out.println(result);
// Jack, Joe, Jane, Mary

– Signature 3:

public static Collector joining(CharSequence delimiter,
                                                             CharSequence prefix,
                                                             CharSequence suffix)

-> Returns a Collector that concatenates the input elements, separated by the specified delimiter, with the specified prefix and suffix.

List strLst = Arrays.asList("Jack", "Joe", "Jane", "Mary");

String result = strLst.stream().collect(Collectors.joining(", ", "names=[", "]"));
System.out.println(result);
// names=[Jack, Joe, Jane, Mary]

Collectors.mapping()

public static <T, U, A, R>
    Collector<T, ?, R> mapping(Function<? super T, ? extends U> mapper,
                               Collector<? super U, A, R> downstream)

-> Adapts a Collector accepting elements of type U to one accepting elements of type T by applying a mapping function to each input element before accumulation.

mapper is a function to be applied to the input elements
downstream is a collector which will accept mapped values

  • T the type of the input elements
  • U type of elements accepted by downstream collector
  • A intermediate accumulation type of the downstream collector
  • R result type of collector

– Example:

List developers= Arrays.asList(new Developer("Jack", 5400),
		new Developer("Joe", 7500),
		new Developer("Jane", 6500),
		new Developer("Mary", 6200)
	);

List names = developers.stream().collect(Collectors.mapping(Developer::getName, Collectors.toList()));
System.out.println(names);
// [Jack, Joe, Jane, Mary]

Collectors.filtering()

public static <T, A, R>
    Collector<T, ?, R> filtering(Predicate<? super T> predicate,
                                 Collector<? super T, A, R> downstream)

-> It adapts a Collector to one accepting elements of the same type T by applying the predicate to each input element and only accumulating if the predicate returns true.

– Example:

List<Integer> intLst = Arrays.asList(1, 2, 3, 4, 2, 5, 6, 4, 8, 2, 7, 8);

List<Integer> oddLst = intLst.stream().collect(Collectors.filtering(i->i%2==1, Collectors.toList()));
System.out.println(oddLst);
// [1, 3, 5, 7]

Collectors.minBy()/maxBy()

Collectors.minBy()

public static <T> Collector<T, ?, Optional<T>>
    minBy(Comparator<? super T> comparator)

-> It returns a Collector that produces the minimal element according to a given Comparator.

List<Integer> intLst = Arrays.asList(2, 14, 4, 17, 5, 6, 1, 8, 2, 7, 8);

Optional<Integer> min = intLst.stream().collect(Collectors.minBy(Comparator.naturalOrder()));

min.ifPresent(System.out::println);
// 1

Collectors.maxBy()

public static <T> Collector<T, ?, Optional<T>>
    maxBy(Comparator<? super T> comparator)

-> It returns a Collector that produces the maximal element according to a given Comparator

List intLst = Arrays.asList(2, 14, 4, 17, 5, 6, 1, 8, 2, 7, 8);

Optional max = intLst.stream().collect(Collectors.maxBy(Comparator.naturalOrder()));

max.ifPresent(System.out::println);
// 17

Collectors.collectingAndThen()

public static<T,A,R,RR> Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream,
                                                                Function<R,RR> finisher)

-> It adapts a Collector to perform an additional finishing transformation.

finisher is a function to be applied to the final result of the downstream collector.

– Example:

List<Developer> developers= Arrays.asList(new Developer("Jack", 5400),
		new Developer("Joe", 7500),
		new Developer("Jane", 6500),
		new Developer("Mary", 6200)
	);

String name = developers.stream().collect(Collectors.collectingAndThen(
									Collectors.maxBy(Comparator.comparing(Developer::getSalary)),
									(Optional<Developer> dev) -> dev.isPresent() ? dev.get().getName() : "none"
								));
System.out.println(name);
// Joe

Collectors.summingInt()/summingLong()/summingDouble()

public static <T> Collector<T, ?, Integer>
    summingInt(ToIntFunction<? super T> mapper)

-> It returns a Collector that produces the sum of a integer-valued function applied to the input elements.
If no elements are present, the result is 0.

– Example:

List intLst = Arrays.asList(2, 14, 4, 17, 5, 6, 1, 8, 2, 7, 8);

Integer total = intLst.stream().collect(Collectors.summingInt(i->i));
System.out.println(total);
// 74

We do summingLong()/summingDouble() as the same way with summingInt().

Collectors.averagingInt()/averagingLong()/averagingDouble()

Signature:

public static <T> Collector<T, ?, Double>
    averagingInt(ToIntFunction<? super T> mapper)

-> Returns a Collector that produces the arithmetic mean of an integer-valued function applied to the input elements. If no elements are present, the result is 0.

averagingInt() example:

List intLst = Arrays.asList(2, 14, 4, 17, 5, 6, 1, 8, 2, 7, 8);

Double average = intLst.stream().collect(Collectors.averagingInt(i->i));
System.out.println(average);
// 6.7272727272727275

We do averagingLong()/averagingDouble() as the same way with averagingInt().

Collectors.reducing()

public static <T> Collector<T, ?, T>
    reducing(T identity, BinaryOperator<T> op)

-> Returns a Collector which performs a reduction of its input elements under a specified BinaryOperator using the provided identity.

– Example:

List<String> strLst = Arrays.asList("gro", "konez", ".com");

String reducingStr = strLst.stream().collect(Collectors.reducing("https://", (subStr, e) -> subStr + e));
System.out.println(reducingStr);
// https://ozenero.com
// 74

Collectors.groupingBy()

public static <T, K> Collector<T, ?, Map<K, List<T>>>
    groupingBy(Function<? super T, ? extends K> classifier)

-> It returns a Collector implementing a “group by” operation on input elements of type T, grouping elements according to a classification function, and returning the results in a Map.

– The collector produces a Map with:
+ Keys are the values resulting from applying the classification function to the input elements
+ Corresponding values are Lists containing the input elements which map to the associated key under the classification function.

– Example:

class Employee{
	private String name;
	private String department;
	
	Employee(String name, String department){
		this.name = name;
		this.department  =department;
	}
	
	public String getName() {
		return this.name;
	}
	
	public String getDepartment() {
		return this.department;
	}
	
	public String toString() {
		return String.format("{name = %s, department = %s}", this.name, this.department);
	}
}

List employees = Arrays.asList(new Employee("Jack", "Software"),
											new Employee("Joe", "Finance"),
											new Employee("Jane", "HR"),
											new Employee("Mary", "Finance"),
											new Employee("Peter", "Software"),
											new Employee("Davis", "Finance")
										);

Map> employeeByDepartment = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment));
System.out.println(employeeByDepartment);
//	{Finance=[{name = Joe, department = Finance}, {name = Mary, department = Finance}, {name = Davis, department = Finance}], 
//		HR=[{name = Jane, department = HR}], 
//		Software=[{name = Jack, department = Software}, {name = Peter, department = Software}]} 

Collectors.partitioningBy()

Collector>> partitioningBy(Predicate predicate)

-> It returns a Collector which partitions the input elements according to a Predicate, and organizes them into a Map.

– Example:

List developers= Arrays.asList(new Developer("Jack", 5400),
		new Developer("Joe", 7500),
		new Developer("Jane", 6500),
		new Developer("Mary", 6200)
	);

Map> partitioningDev = developers.stream().collect(Collectors.partitioningBy(dev -> dev.getSalary() > 6300));

System.out.println(partitioningDev);
// {false=[{name = Jack, salary = 5400}, {name = Mary, salary = 6200}], true=[{name = Joe, salary = 7500}, {name = Jane, salary = 6500}]}

Collectors.summarizingInt()

public static 
    Collector summarizingInt(ToIntFunction mapper)

-> It returns a Collector which applies an int-producing mapping function to each input element, and returns summary statistics for the resulting values.

IntSummaryStatistics statistics = developers.stream().collect(Collectors.summarizingInt(Developer::getSalary));
 
String statisticsStr = String.format("count = %d, min = %d, max = %d, sum = %d, average = %.2f", 
		 								statistics.getCount(), statistics.getMin(), 
		 								statistics.getMax(), statistics.getSum(), statistics.getAverage());
 
System.out.println(statisticsStr); 
// count = 4, min = 5400, max = 7500, sum = 25600, average = 6400,00

We also have summarizingLong(), summarizingDouble() and apply them in programming as the same way doing with summarizingInt.

Conclusion

We had learn how to use Java 8 Stream Collector APIs to perform mutable fold operations:

  • How to use Stream.collect() methods with difference signatures APIs
  • Apply Stream.collect() with many java.util.stream.Collectors()‘s implementation: toList(), toSet(), toMap(), summarizingInt(), partitioningBy(), groupingBy(), …

Happy learning! See you later!

4.5 2 votes
Article Rating
Subscribe
Notify of
guest
4.3K Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments