Question
What is the difference between these Kotlin declarations?
private val repositories = mutableListOf<String>()
private val repositories = ArrayList<String>()
Both create mutable lists. Why does Kotlin provide both mutableListOf() and ArrayList()? Is there a significant difference, and when should each one be used?
Short Answer
mutableListOf() is a Kotlin factory function that creates a MutableList, while ArrayList() directly creates an ArrayList implementation. In most everyday Kotlin code, prefer mutableListOf() unless you specifically need an ArrayList type or behavior.
Concept
A list interface describes what a collection can do: store items, add items, remove items, and access items by index. An implementation describes how that list is built internally.
In Kotlin:
MutableList<T>is an interface for a list whose contents can change.ArrayList<T>is one concrete implementation of a mutable list.mutableListOf<T>()is a factory function that creates a mutable list and exposes it asMutableList<T>.
val names: MutableList<String> = mutableListOf()
val names2: ArrayList<String> = ArrayList()
On the JVM, mutableListOf() commonly creates an ArrayList for lists with multiple elements. However, its important promise is its return type: MutableList<T>, not a particular internal implementation.
This distinction matters because code is usually easier to maintain when it depends on the general capability it needs (MutableList) rather than a specific data structure (ArrayList).
Mental Model
Think of MutableList as a job description: “a container that can hold ordered items and be changed.”
ArrayList is one employee who can do that job. Another implementation, such as LinkedList on the JVM, can also do the job differently.
mutableListOf()says: “Give me a mutable list.”ArrayList()says: “Give me specifically the array-backed kind of mutable list.”
Most of the time, you care that the container is mutable, not which employee performs the work. In that case, use mutableListOf().
Syntax and Examples
Create an empty mutable list with Kotlin's factory function:
val repositories = mutableListOf<String>()
repositories.add("kotlin")
repositories.add("ktor")
println(repositories) // [kotlin, ktor]
Create an empty ArrayList directly:
val repositories = ArrayList<String>()
repositories.add("kotlin")
repositories.add("ktor")
println(repositories) // [kotlin, ktor]
Both examples allow add, remove, and index assignment:
repositories[0] = "kotlinx"
You can also provide starting elements:
val languages = mutableListOf("Kotlin", "Java")
val tools = ArrayList(listOf("Gradle", "Git"))
A common Kotlin style is to declare the interface type explicitly when it improves clarity:
private val repositories: MutableList<String> = mutableListOf()
The val means the repositories property cannot point to a different list later. It does not make the list contents immutable.
Step by Step Execution
Consider this code:
private val repositories = mutableListOf<String>()
fun addRepository(name: String) {
repositories.add(name)
}
fun main() {
addRepository("kotlin")
addRepository("compose")
println(repositories)
}
Execution proceeds as follows:
mutableListOf<String>()creates an empty mutable list.- The
repositoriesproperty stores a reference to that list. addRepository("kotlin")callsadd, so the list becomes["kotlin"].addRepository("compose")adds another item, so the list becomes["kotlin", "compose"].printlnoutputs:
[kotlin, compose]
Although repositories is declared with val, add is allowed because the existing list is being changed. Reassignment is not allowed:
// repositories = mutableListOf("new") // Error: val cannot be reassigned
Real World Use Cases
Mutable lists are useful when an application builds or updates an ordered collection over time.
- API processing: collect valid records while reading a response.
- Form validation: accumulate validation messages before showing them to a user.
- UI state: maintain a list of selected items or draft entries.
- File processing: store lines, paths, or parsed results while reading files.
- Repository or service layers: collect database results before returning them.
For example, collecting validation errors:
fun validateUsername(username: String): List<String> {
val errors = mutableListOf<String>()
if (username.isBlank()) errors.add("Username is required")
if (username.length < 3) errors.add("Username must contain at least 3 characters")
return errors
}
Here, the function uses a mutable list while building the result, but returns List<String> so callers only need read access.
Real Codebase Usage
In production Kotlin code, a typical pattern is:
- Build data with a mutable local collection.
- Return or expose it as the read-only
Listinterface when mutation should not be part of the public API.
fun activeUserNames(users: List<User>): List<String> {
val names = mutableListOf<String>()
for (user in users) {
if (!user.isActive) continue
names.add(user.name)
}
return names
}
For simple transformations, map and filter can often avoid explicit mutation:
fun activeUserNames(users: List<User>): List<String> =
users.filter { it.isActive }
.map { it.name }
Developers generally use ArrayList() directly when they have a specific reason, such as:
- A Java API requires an
ArrayListparameter. - They want the variable or function signature to explicitly require
ArrayList. - They need an
ArrayListconstructor, such as copying another collection withArrayList(existingItems).
For ordinary application logic, mutableListOf() communicates intent better: the code needs a mutable list, not necessarily a particular implementation.
Common Mistakes
Assuming val makes a list immutable
This list can still change:
val items = mutableListOf("one")
items.add("two") // Valid
val prevents reassignment only. Use listOf() when you want a read-only list reference:
val items = listOf("one", "two")
// items.add("three") // Does not compile
Assuming List guarantees deep immutability
List is read-only through that reference, but the underlying object could still be mutable elsewhere:
val mutable = mutableListOf("one")
val readOnly: List<String> = mutable
mutable.add("two")
println(readOnly) // [one, two]
If you need an independent snapshot, copy it:
val snapshot = mutable.toList()
Exposing a mutable property unnecessarily
Avoid making internal mutable state easy for every caller to change:
class Project {
val repositories = mutableListOf<String>()
}
Prefer keeping it private and exposing a read-only view:
class Project {
private val mutableRepositories = mutableListOf<String>()
val repositories: List<String> get() = mutableRepositories
}
Using ArrayList when only MutableList is needed
This is valid, but more specific than necessary:
val names: ArrayList<String> = ArrayList()
Prefer this unless ArrayList itself is required:
val names: MutableList<String> = mutableListOf()
Comparisons
| Feature | mutableListOf<T>() | ArrayList<T>() |
|---|---|---|
| What it is | Kotlin factory function | Concrete list implementation constructor |
| Usual exposed type | MutableList<T> | ArrayList<T> |
| Can contents change? | Yes | Yes |
| Typical use | Default choice for mutable lists | When a specific ArrayList is required |
| Implementation guarantee | Do not rely on a particular implementation | Explicitly an ArrayList |
| Kotlin style | Usually preferred for general code | Useful for implementation-specific cases |
Related collection factories:
| Expression | Resulting intent |
|---|---|
listOf("a") | Read-only list interface |
mutableListOf("a") | Mutable list interface |
arrayListOf("a") | An ArrayList directly |
ArrayList<String>() | Empty ArrayList directly |
arrayListOf() and ArrayList() both explicitly request an ArrayList; the former is a Kotlin factory function and is convenient when supplying elements.
Cheat Sheet
// Read-only list reference
val readOnly = listOf("a", "b")
// General mutable list: preferred default
val mutable = mutableListOf("a", "b")
// Specific concrete implementation
val arrayList = ArrayList<String>()
arrayList.add("a")
// Copy items into a new ArrayList
val copied = ArrayList(readOnly)
- Use
mutableListOf()when you need to add, remove, or replace elements. - Use
ArrayList()when a specificArrayListis required. valprevents assigning a new list; it does not prevent changing a mutable list's contents.- Prefer returning
List<T>from functions when callers should only read the result. - Do not rely on
mutableListOf()using a particular implementation unless its documented contract requires it.
FAQ
Is mutableListOf() an ArrayList in Kotlin?
On the JVM, it commonly uses an ArrayList implementation for normal mutable lists. However, write code against MutableList unless you specifically need ArrayList.
Which should I use: mutableListOf() or ArrayList()?
Use mutableListOf() by default. Use ArrayList() when an API requires that concrete type or when you deliberately need an ArrayList.
Is arrayListOf() the same as ArrayList()?
Both create an ArrayList. arrayListOf() is a Kotlin factory function that is convenient for creating a list with initial elements.
Does val repositories = mutableListOf<String>() create an immutable list?
No. You can add or remove elements. val only prevents assigning another list to repositories.
Can I pass a MutableList where an ArrayList is expected?
Not necessarily. A MutableList may use a different implementation. If an API requires ArrayList, create or copy one with ArrayList(items).
Should functions return MutableList?
Return List when callers only need to read data. Return MutableList only when allowing callers to modify the returned collection is intentional.
Is List completely immutable in Kotlin?
List is read-only through its type. It does not guarantee that another reference cannot mutate the underlying collection.
Mini Project
Description
Create a small repository tracker that stores repository names internally and exposes a read-only list to callers. This demonstrates using mutableListOf() for implementation details while protecting mutable state with the List interface.
Goal
Build a class that can add unique repository names and provide a safe read-only snapshot of the stored names.
Requirements
Use a private mutable list to store repository names.
Add a function that ignores blank names and duplicate names.
Add a function that returns the repository names as List<String>.
Demonstrate adding several names and printing the final result.
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.
Add a Maven Repository URL in Gradle Kotlin DSL (build.gradle.kts)
Learn how to add Maven Central and a custom Maven repository URL in Gradle Kotlin DSL using build.gradle.kts syntax.
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.