Question

Difference Between List and Array in Kotlin

kotlinarrayslist

Question

In Kotlin, what is the difference between List and Array?

They seem to support many of the same operations, such as looping and using functions like filter. Is there any real difference in behavior, performance, or typical usage?

val names1 = listOf("Joe", "Ben", "Thomas")
val names2 = arrayOf("Joe", "Ben", "Thomas")

for (name in names1)
    println(name)

for (name in names2)
    println(name)

When should I use a List instead of an Array, and vice versa?

Short Answer

By the end of this page, you will understand how List and Array differ in Kotlin, even though they often look similar in simple examples. You will learn how they differ in size behavior, mutability, APIs, performance characteristics, interoperability, and common real-world usage so you can choose the right one confidently.

Concept

List and Array in Kotlin are both ordered collections, but they are not the same kind of type.

  • An Array is a fixed-size container that stores elements by index.
  • A List is a collection interface designed for working with ordered data at a higher level.

The key difference

The biggest practical difference is this:

  • Array has a fixed size after creation.
  • List represents a read-only list interface in Kotlin.

That means:

val arr = arrayOf("A", "B", "C")
arr[0] = "Z"      // allowed
// arr.add("D")   // not allowed
val list = listOf("A", "B", "C")
// list[0] = "Z"  // not allowed
// list.add("D")  // not allowed

If you want a resizable list, Kotlin provides MutableList:

val items = mutableListOf("A", "B", "C")
items.add("D")
items[0] = "Z"

Why they can feel similar

They both support:

  • iteration with for
  • indexing
  • many standard library functions like filter, map, and forEach

This is why they often look interchangeable in beginner examples.

Why the distinction matters

In real programs, choosing the right type affects:

  • whether the size can change
  • whether elements can be replaced
  • how your API communicates intent
  • Java interoperability
  • performance in specific cases

Important Kotlin design idea

Kotlin encourages developers to program to collection interfaces like List instead of concrete storage types like Array, unless array-specific behavior is actually needed.

That usually makes code easier to read, safer to use, and more flexible.

Mental Model

Think of an Array like a row of numbered lockers.

  • The number of lockers is fixed.
  • You can open locker 0, 1, or 2 and replace what is inside.
  • But you cannot suddenly create more lockers in the same row.

Think of a List like a playlist view.

  • You usually care about the ordered sequence of items.
  • A read-only List says: “Here are the songs you can look at.”
  • A MutableList says: “You may also add, remove, or reorder songs.”

So:

  • Use Array when you need a fixed-size indexed container.
  • Use List when you want to represent ordered data in a more general and idiomatic Kotlin way.
Take Quiz

Syntax and Examples

Creating each type

val numbersArray = arrayOf(1, 2, 3)
val numbersList = listOf(1, 2, 3)
val mutableNumbers = mutableListOf(1, 2, 3)

Accessing elements

println(numbersArray[0]) // 1
println(numbersList[0])  // 1

Changing elements

val colors = arrayOf("red", "green", "blue")
colors[1] = "yellow"
println(colors.joinToString()) // red, yellow, blue

A read-only List does not allow element replacement:

val names = listOf("Joe", "Ben", "Thomas")
// names[0] = "Sam" // error

A MutableList does:

val names = mutableListOf("Joe", "Ben", "Thomas")
names[0] = "Sam"
names.add("Lina")
println(names) // [Sam, Ben, Thomas, Lina]

Similar operations on both

Both arrays and lists work with many collection-style operations:

val arr = arrayOf("Joe", "Ben", "Thomas")
val list = listOf("Joe", "Ben", "Thomas")

println(arr.filter { it.startsWith("T") })   // [Thomas]
println(list.filter { it.startsWith("T") })  // [Thomas]

Converting between them

val arr = arrayOf("a", "b", "c")
val list = arr.toList()

val list2 = listOf("x", "y", "z")
val arr2 = list2.toTypedArray()

Primitive arrays

Kotlin also has special primitive array types:

val intArray = intArrayOf(1, 2, 3)

These are different from Array<Int> and are useful for performance and Java interop.

Step by Step Execution

Consider this example:

val namesArray = arrayOf("Joe", "Ben", "Thomas")
val namesList = listOf("Joe", "Ben", "Thomas")

println(namesArray[1])
println(namesList[1])

val filteredArray = namesArray.filter { it.length > 3 }
val filteredList = namesList.filter { it.length > 3 }

println(filteredArray)
println(filteredList)

What happens step by step

  1. namesArray is created as an Array<String> with 3 fixed slots.
  2. namesList is created as a read-only List<String> with 3 elements.
  3. namesArray[1] reads the value at index 1, which is "Ben".
  4. namesList[1] also reads the value at index 1, which is "Ben".
  5. filter { it.length > 3 } checks each element.
    • "Joe" has length 3, so it is skipped.
    • "Ben" has length 3, so it is skipped.
    • "Thomas" has length 6, so it is kept.
  6. filteredArray becomes a List<String>, not an array.
  7. filteredList also becomes a List<String>.

Important detail

Even when you start with an array:

val result = namesArray.filter { it.startsWith("J") }

result is a List<String>, because many Kotlin standard functions return collection types rather than preserving Array.

That is one reason arrays and lists may feel similar at first, but their core types still differ.

Real World Use Cases

When List is commonly used

List is the default choice in most Kotlin application code.

Examples

  • Returning a list of users from a service
  • Passing a collection of validation errors
  • Working with API results
  • Rendering ordered UI items
  • Filtering and mapping business data
fun getActiveUsers(): List<String> {
    return listOf("Ana", "Mila", "Ravi")
}

When Array is commonly used

Array is more common when fixed-size storage or interoperability matters.

Examples

  • Interacting with Java APIs that expect arrays
  • Storing a fixed number of values
  • Working with matrix-like indexed data
  • Performance-sensitive code with primitive arrays
  • Using vararg-related operations
val rgb = arrayOf(255, 128, 64)
println(rgb[0])

When MutableList is better than both

If you need to add and remove items dynamically, use MutableList.

val tasks = mutableListOf("Write", "Test")
tasks.add("Deploy")
tasks.remove("Test")
Take Quiz

Real Codebase Usage

In real Kotlin codebases, developers usually prefer List in public APIs and only use Array when there is a specific reason.

Common pattern: expose List, keep flexibility

fun loadNames(): List<String> {
    return listOf("Joe", "Ben", "Thomas")
}

Why this is common:

  • callers only need to read the values
  • implementation can later change internally
  • the API clearly communicates intent

Common pattern: use MutableList during construction

fun collectErrors(input: List<String>): List<String> {
    val errors = mutableListOf<String>()

    for (item in input) {
        if (item.isBlank()) {
            errors.add("Blank value found")
        }
    }

    return errors
}

This pattern is common because:

  • building data often needs mutation
  • returning List avoids exposing mutation unnecessarily

Common pattern: arrays for vararg and Java interop

fun printAll(vararg names: String) {
    for (name in names) println(name)
}

val arr = arrayOf("Joe", "Ben")
printAll(*arr)

The spread operator * is often used when passing an array to a vararg parameter.

Common pattern: primitive arrays in performance-sensitive code

val scores = intArrayOf(10, 20, 30)

Primitive arrays avoid boxing overhead that can happen with List<Int> or Array<Int> in some cases.

Guard clauses and validation with lists

fun processNames(names: List<String>) {
    if (names.isEmpty()) return

    val validNames = names.filter { it.isNotBlank() }
    println(validNames)
}

This is more idiomatic than requiring arrays unless the function truly needs one.

Common Mistakes

1. Thinking List and MutableList are the same

Broken expectation:

val items = listOf("a", "b")
// items.add("c") // error

Why it happens:

  • listOf() gives a read-only List
  • it does not support add or remove

Use this instead:

val items = mutableListOf("a", "b")
items.add("c")

2. Thinking arrays are resizable

Broken code:

val arr = arrayOf(1, 2, 3)
// arr.add(4) // error

Why it happens:

  • arrays have fixed size
  • you can replace existing elements, but not grow the array directly

3. Assuming filter() on an array returns an array

Broken assumption:

val arr = arrayOf("a", "bb", "ccc")
val result = arr.filter { it.length > 1 }

result is a List<String>, not an Array<String>.

If you need an array afterward:

val resultArray = arr.filter { it.length > 1 }.toTypedArray()

4. Confusing read-only with truly immutable

val list: List<String> = mutableListOf("a", "b")

This reference is read-only, but the underlying object may still be mutable if another reference can change it.

5. Using Array<Int> when a primitive array is better

val a = Array(3) { it }
val b = intArrayOf(0, 1, 2)

For many numeric cases, IntArray is more efficient than Array<Int>.

6. Exposing arrays in APIs unnecessarily

Less idiomatic:

fun getNames(): Array<String> = arrayOf("Joe", "Ben")

Often better:

fun getNames(): List<String> = listOf("Joe", "Ben")

Use arrays only when array behavior is specifically required.

Comparisons

FeatureList<T>MutableList<T>Array<T>
Ordered collectionYesYesYes
Access by indexYesYesYes
Replace element by indexNoYesYes
Add/remove elementsNoYesNo
Fixed sizeInterface does not imply storage detailsNo, can grow/shrinkYes
Common in Kotlin APIsYesSometimes internallyLess often
Good for Java interopSometimesSometimesYes
Primitive specialized versionsNoNoYes, like IntArray

Array<T> vs List<T>

  • Choose List<T> when you want to expose ordered data for reading.
  • Choose Array<T> when you need fixed-size indexed storage or Java-style array behavior.

List<T> vs MutableList<T>

  • Choose List<T> when callers should not modify the collection.
  • Choose MutableList<T> when you need to add, remove, or update elements.

Array<Int> vs IntArray

TypeStoresTypical use
Array<Int>boxed Int objectsgeneral generic APIs
IntArrayprimitive int valuesperformance-sensitive numeric work
Take Quiz

Cheat Sheet

Quick rules

  • listOf(...) creates a read-only List
  • mutableListOf(...) creates a MutableList
  • arrayOf(...) creates an Array
  • Array has fixed size
  • List cannot be modified through its read-only API
  • MutableList supports add, remove, and element assignment

Common syntax

val list = listOf("a", "b")
val mutable = mutableListOf("a", "b")
val array = arrayOf("a", "b")

Read values

println(list[0])
println(array[0])

Update values

mutable[0] = "x"
array[0] = "x"

Add/remove

mutable.add("c")
mutable.remove("a")

Convert

val listFromArray = array.toList()
val arrayFromList = list.toTypedArray()

Remember

  • filter, map, and similar operations often return List
  • use List by default in Kotlin APIs
  • use Array when fixed-size storage or interop is needed
  • use IntArray, DoubleArray, etc. for primitive performance cases

FAQ

Is Kotlin List the same as Java array?

No. A Kotlin List is a collection interface for ordered elements, while an array is a fixed-size indexed container.

Can I change elements inside a Kotlin List?

Not if it is a read-only List. Use MutableList if you need to replace, add, or remove elements.

Why do Array and List both work in for loops?

Because both are iterable in Kotlin, so the for syntax works naturally with each.

Does filter() return the same type I started with?

Not always. For arrays, filter() usually returns a List, not an array.

Which should I use by default in Kotlin?

Usually List, especially for function parameters and return types, unless you specifically need array behavior.

When should I use Array in Kotlin?

Use it for fixed-size indexed data, Java interoperability, vararg-related operations, or specialized array use cases.

What is the difference between Array<Int> and IntArray?

Array<Int> stores boxed Int values, while IntArray stores primitive integers and is often more efficient.

Take Quiz

Mini Project

Description

Build a small Kotlin utility that stores a fixed set of weekday names in an array and manages a changing task list with a mutable list. This demonstrates the real difference between fixed-size data and dynamically changing collections.

Goal

Create a program that uses an Array for fixed data and a MutableList for editable data, then prints useful results from both.

Requirements

  • Create an Array containing the seven weekday names.
  • Create a MutableList containing at least two tasks.
  • Update one existing weekday or task by index.
  • Add a new task and remove one old task.
  • Print all weekdays and final tasks.
  • Use at least one collection function such as filter or map.
Take Quiz

Keep learning

Related questions

View more