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()); } } Listdevelopers= 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 super T, A, R> 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:
ListintLst = 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.
ListintLst = 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; } } Listdevelopers= 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:
ListstrLst = Arrays.asList("https://", "gro", "konez", ".com"); String result = strLst.stream().collect(Collectors.joining()); System.out.println(result); // https://ozenero.com
– Signature 2:
public static Collectorjoining(CharSequence delimiter)
-> It returns a Collector that concatenates the input elements, separated by the specified delimiter, in encounter order.
Example:
ListstrLst = 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 Collectorjoining(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.
ListstrLst = 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:
Listdevelopers= 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
ListintLst = 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:
ListintLst = 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:
ListintLst = 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); } } Listemployees = 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 super T> predicate)
-> It returns a Collector which partitions the input elements according to a Predicate, and organizes them into a Map.
– Example:
Listdevelopers= 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 staticCollector summarizingInt(ToIntFunction super T> 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 manyjava.util.stream.Collectors()
‘s implementation:toList()
,toSet()
,toMap()
,summarizingInt()
,partitioningBy()
,groupingBy()
, …
Happy learning! See you later!