Question

mutableListOf vs ArrayList in Kotlin: Differences and Usage

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 as MutableList<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().

Take Quiz

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:

  1. mutableListOf<String>() creates an empty mutable list.
  2. The repositories property stores a reference to that list.
  3. addRepository("kotlin") calls add, so the list becomes ["kotlin"].
  4. addRepository("compose") adds another item, so the list becomes ["kotlin", "compose"].
  5. println outputs:
[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.

Take Quiz

Real Codebase Usage

In production Kotlin code, a typical pattern is:

  1. Build data with a mutable local collection.
  2. Return or expose it as the read-only List interface 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 ArrayList parameter.
  • They want the variable or function signature to explicitly require ArrayList.
  • They need an ArrayList constructor, such as copying another collection with ArrayList(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

FeaturemutableListOf<T>()ArrayList<T>()
What it isKotlin factory functionConcrete list implementation constructor
Usual exposed typeMutableList<T>ArrayList<T>
Can contents change?YesYes
Typical useDefault choice for mutable listsWhen a specific ArrayList is required
Implementation guaranteeDo not rely on a particular implementationExplicitly an ArrayList
Kotlin styleUsually preferred for general codeUseful for implementation-specific cases

Related collection factories:

ExpressionResulting 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.

Take Quiz

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 specific ArrayList is required.
  • val prevents 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.

Take Quiz

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.

Take Quiz

Keep learning

Related questions

View more