Question
Java 8 Stream.collect Equivalents in Kotlin Standard Library
Question
In Java 8, Stream.collect(...) is commonly used to aggregate and transform data in many ways. In Kotlin, there is no single direct equivalent with the same API shape, and instead similar behavior is usually provided through standard library collection operations and extension functions.
When converting Java examples to Kotlin, especially examples from Collectors, it can be unclear which Kotlin functions match common collect use cases.
For example, what are the idiomatic Kotlin standard library equivalents for these common Java Stream.collect patterns?
- Accumulate names into a
List - Accumulate names into a
TreeSet - Convert elements to strings and concatenate them with commas
- Compute the sum of employee salaries
- Group employees by department
- Compute the sum of salaries by department
- Partition students into passing and failing
The goal is to understand how Kotlin expresses these collection operations using its standard library rather than Java 8 stream collectors.
Short Answer
By the end of this page, you will understand that Kotlin usually replaces Java's single Stream.collect(...) entry point with many focused collection functions such as toList(), toSet(), toCollection(), joinToString(), sumOf(), groupBy(), groupingBy(), fold(), and partition(). You will also see how to choose the right Kotlin function for common aggregation tasks like collecting, grouping, summing, and splitting data.
Concept
Java Streams centralize many aggregation operations through collect(...) and predefined Collectors. Kotlin takes a different approach.
Instead of one large collecting API, Kotlin provides many small, readable extension functions directly on collections and sequences. This often makes Kotlin code shorter and easier to read because the operation name describes the result directly.
For example:
-
Java:
stream.collect(toList()) -
Kotlin:
toList() -
Java:
stream.collect(joining(",")) -
Kotlin:
joinToString(",") -
Java:
stream.collect(groupingBy(...)) -
Kotlin:
groupBy { ... }
This matters because in real Kotlin code, developers usually do not look for one universal collector function. Instead, they choose the operation that matches the goal:
- Build a collection:
toList(),toSet(),toCollection(...) - Convert to text:
joinToString()
Mental Model
Think of Java collect(...) like going to a service desk and handing over a form that says what result you want.
Kotlin works more like using clearly labeled buttons:
- Want a list? Press
toList() - Want a set? Press
toSet()ortoCollection(...) - Want grouped data? Press
groupBy() - Want a total? Press
sumOf() - Want a comma-separated string? Press
joinToString()
Instead of one desk handling everything, Kotlin gives you a tool drawer where each tool has a clear purpose. That usually makes code easier to read because the operation is named directly in the call.
Syntax and Examples
Core Kotlin equivalents
Here are the most common Kotlin replacements for Java Stream.collect(...) use cases.
data class Employee(val name: String, val department: String, val salary: Int)
data class Student(val name: String, val passing: Boolean)
val employees = listOf(
Employee("Alice", "Engineering", 100_000),
Employee("Bob", "Engineering", 90_000),
Employee("Cara", "HR", 70_000)
)
val students = listOf(
Student("Mina", true),
Student("Leo", false),
Student("Nora", true)
)
Accumulate names into a List
val names = employees.map { it.name }.toList()
If employees.map { it.name } already returns a , then may be unnecessary:
Step by Step Execution
Consider this Kotlin example:
data class Employee(val name: String, val department: String, val salary: Int)
val employees = listOf(
Employee("Alice", "Engineering", 100),
Employee("Bob", "Engineering", 80),
Employee("Cara", "HR", 70)
)
val result = employees
.groupBy { it.department }
.mapValues { (_, staff) -> staff.sumOf { it.salary } }
println(result)
Step-by-step
employeesis a list with threeEmployeeobjects.groupBy { it.department }reads each employee and groups them by department.- After
groupBy, the intermediate result is roughly:
mapOf(
"Engineering" to listOf(Employee("Alice", "Engineering", 100), Employee("Bob", "Engineering", 80)),
to listOf(Employee(, , ))
)
Real World Use Cases
Common real uses of these Kotlin operations
Building API response data
You may group database results before returning JSON:
val usersByRole = users.groupBy { it.role }
Generating CSV-like output
When exporting data or building logs:
val line = items.joinToString(",") { it.id.toString() }
Calculating totals
In shopping carts, invoices, payroll, or analytics:
val total = cartItems.sumOf { it.priceInCents * it.quantity }
Splitting valid and invalid records
Useful in import scripts and validation pipelines:
val (valid, invalid) = records.partition { it.isValid() }
Collecting into a specific collection type
If order or uniqueness matters:
val ids = rows.map { it.id }.toMutableSet()
val sortedTags = tags.toCollection(java.util.TreeSet())
Aggregating metrics by category
For reports and dashboards:
Real Codebase Usage
In real Kotlin projects, developers usually combine small collection operations to express business logic clearly.
Common patterns
1. Mapping before collecting
val usernames = users.map { it.username }
This is often enough. If the source is already a collection, no extra terminal step is needed.
2. Collecting into a specific target
val uniqueNames = users.map { it.name }.toSet()
val sortedNames = users.map { it.name }.toCollection(java.util.TreeSet())
3. Group then transform
val ordersByCustomer = orders.groupBy { it.customerId }
val totalsByCustomer = ordersByCustomer.mapValues { (_, orders) ->
orders.sumOf { it.totalInCents }
}
4. One-pass grouped aggregation with groupingBy()
val countsByStatus = tasks.groupingBy { it.status }.eachCount()
Or:
val pointsByTeam = players.groupingBy { it.team }
.fold(0) { acc, player -> acc + player.points }
This is useful when you want aggregation behavior closer to Java collectors.
Common Mistakes
1. Looking for one exact collect(...) replacement
Beginners often expect Kotlin to have a direct equivalent to Java's full collector API.
Mistake
// There is no single stdlib method exactly like Java's Stream.collect(Collectors...)
Better approach
Pick the function that matches the result:
toList()toSet()toCollection(...)joinToString()sumOf()groupBy()groupingBy()partition()
2. Using groupBy() when you only need counts or sums
Less efficient style
val countByDept = employees.groupBy { it.department }
.mapValues { (_, staff) -> staff.size }
Better style
Comparisons
Kotlin equivalents for common Java collector patterns
| Java Stream / Collector | Kotlin stdlib equivalent | Notes |
|---|---|---|
stream.collect(toList()) | toList() or just map(...) result | map() on a collection already returns a list |
stream.collect(toSet()) | toSet() | Creates a set of unique elements |
stream.collect(toCollection(TreeSet::new)) | toCollection(TreeSet()) | Use when destination type matters |
stream.collect(joining(",")) | joinToString(",") | Can transform each element with a lambda |
Cheat Sheet
Quick reference
Collect into common types
val list = items.toList()
val set = items.toSet()
val sortedSet = items.toCollection(java.util.TreeSet())
Transform then collect
val names = employees.map { it.name }
val ids = employees.map { it.id }.toSet()
Join into a string
val text = items.joinToString(",")
val names = employees.joinToString(", ") { it.name }
Sum values
val total = employees.sumOf { it.salary }
Group items
val byDept = employees.groupBy { it.department }
Group and count
val counts = employees.groupingBy { it.department }.eachCount()
Group and sum
FAQ
What is the Kotlin equivalent of Java Stream.collect(toList())?
Usually toList(), or no extra call at all if you already used map() on a collection, because map() returns a list.
Does Kotlin have a direct equivalent to Java Collectors?
Not as one single API. Kotlin standard library provides many separate functions like groupBy(), sumOf(), and joinToString() instead.
How do I group and sum in Kotlin?
Use either:
items.groupBy { it.key }.mapValues { (_, values) -> values.sumOf { it.amount } }
or:
items.groupingBy { it.key }.fold(0) { acc, item -> acc + item.amount }
When should I use groupBy() instead of groupingBy()?
Use groupBy() when you need the actual grouped lists. Use groupingBy() when you mainly want aggregated results like counts or sums.
Mini Project
Description
Create a small reporting utility for a company. You have a list of employees, and you want to generate several useful summaries from it using Kotlin standard library functions instead of Java stream collectors. This project demonstrates collecting into lists and sets, joining names, summing salaries, grouping by department, and partitioning employees by salary threshold.
Goal
Build a Kotlin program that produces multiple summary results from one employee list using idiomatic Kotlin collection operations.
Requirements
- Define an
Employeedata class withname,department, andsalary - Create a sample list of employees in at least two departments
- Print a comma-separated list of employee names
- Print total salary across all employees
- Print salary totals grouped by department
- Partition employees into high-paid and lower-paid groups based on a salary threshold
Keep learning
Related questions
Accessing Kotlin Extension Functions from Java
Learn how Kotlin extension functions are compiled and how to call them correctly from Java with clear examples and common pitfalls.
Allow HTTP and HTTPS in Android 9 Pie with Network Security Configuration
Learn how Android 9 Pie handles cleartext HTTP traffic and how to allow HTTP and HTTPS safely using network security config.
Android AlarmManager Example: Scheduling Tasks with AlarmManager
Learn how to use Android AlarmManager to schedule tasks, set alarms, and handle broadcasts with a simple beginner example.