1. Overview
In this tutorial, we’re going to compare Array
2. Array vs. List
2.1. Data Structure
From a data structure point of view, the Kotlin compiler compiles the Array
For instance, let’s consider a simple example:
val cities = arrayOf("Tehran", "Sari", "Neka")
The Kotlin compiler (kotlinc) compiles this into bytecode that, when viewed using javap, looks like:
0: iconst_3
1: anewarray #8 // class java/lang/String
The anewarray opcode creates an array of reference types. Also, the iconst_3 pushes integer 3 to the operand stack. Later on, the anewarray instruction will use this integer as the array length.
On the other hand, the List
In addition to ArrayList
As opposed to arrays in Kotlin, the memory representation of lists in Kotlin can vary, as they’re completely up to the concrete implementations.
2.2. Mutability
Arrays in Kotlin are always mutable, as we can replace the value of each segment with something new:
cities[0] = "Berlin"
But we can’t prepend or append new values to arrays as they’re fixed-sized.
On the other hand, the List
If we want mutability, we should use MutableList
val colors = mutableListOf("Blue") // [Blue]
colors[0] = "Green" // replace: [Green]
colors.add(0, "Red") // prepend: [Red, Green]
colors.add("Blue") // append: [Red, Green, Blue]
As opposed to Array
2.3. Generic Variance
The concept of variance in terms of generics determines how generic types with the same base type and different type parameters relate to each other.
For instance, we all know that String is a subtype of Any in Kotlin. Now given that, what’s the relationship between List
public interface List<out T> : Collection<T> {
override fun iterator(): Iterator<T>
public operator fun get(index: Int): T
// omitted
}
Since the type argument T is always used in the out positions, the List
val colors = listOf("Red")
val colorsAsAny: List<Any> = colors // this works
Because of the covariance, here we’re assigning a value of List
On the other hand, the same thing is not true for MutableList
public interface MutableList<T> : List<T>, MutableCollection<T> {
override fun add(element: T): Boolean
override fun remove(element: T): Boolean
// omitted
}
So the MutableList
val colors = mutableListOf("Red")
val colorsAsAny: MutableList<Any> = colors
Because of the invariance, the above snippet won’t even compile. Quite similar to MutableList
val colors: Array<Any> = arrayOf<String>("Red")
The variance of these types can be confusing, especially if we’re coming from a Java background. In Java, generic types are invariant and arrays are covariant!
2.4. Specialized Primitives
In Kotlin, we have special functions to create arrays of primitive types:
val bytes = byteArrayOf(42)
val shorts = shortArrayOf(42)
val ints = intArrayOf(42)
val longs = longArrayOf(42)
val floats = floatArrayOf(42.0f)
val doubles = doubleArrayOf(42.0)
val chars = charArrayOf('a')
val booleans = booleanArrayOf(true)
This is mostly because the JVM has special opcodes to create and manipulate such arrays:
0: iconst_1
1: newarray byte
Here, the bytecode uses the newarray opcode (as opposed to anewarray) to create an array of bytes. The bottom line is, arrays have optimized representations for primitive data types, making them more suitable for some performance-sensitive cases.
On the other hand, there is no optimized version for primitives in lists in the Kotlin standard library.
2.5. Other Subtle Differences
To have good interoperability with Java, Kotlin treats some Java types specifically. Such types are not loaded from Java “as is”, but are mapped to corresponding Kotlin types. More specifically, List
In addition to all these, there are some even more subtle differences between the two. For instance, the “==” operator is using reference equality for arrays and content equality for lists:
println(intArrayOf(1) == intArrayOf(1)) // false
println(listOf(1) == listOf(1)) // true
3. Conclusion
In this article, we enumerated the differences between arrays and lists in Kotlin. To sum up, arrays are fixed-sized sequences of elements, they are mutable and invariant in terms of generics, and they come with more optimized primitive versions. Moreover, they’re taking advantage of JVM special treatments for arrays.
On the other hand, List