A Kotlin array holds a fixed number of positions. You can replace the value at a position, but adding another position requires a new array. That makes an array different from MutableList, even though both support indexing and loops. Arrays are useful at Java boundaries and for numeric buffers whose size is known.
The existing lists lesson introduces the comparison. This reference goes further into Array, IntArray, nested shapes and content comparisons. You will build a weekly inventory adjustment and learn why a val array can still change. The examples run as separate Kotlin/JVM programs with only the standard library.
Array and primitive-array storage
Use arrayOf for ordinary values and Array(size) with an initializer when each position is calculated. Array<String> holds strings, while Array<Int> uses the generic array representation. Kotlin also provides specialized numeric arrays such as IntArray, DoubleArray and LongArray. On the JVM these correspond to primitive Java arrays and avoid boxing each primitive element into an object.
IntArray is not a subtype of Array<Int>. They offer similar operations but are distinct types, so a function expecting one does not automatically accept the other. Convert deliberately with toTypedArray or toIntArray when required. Conversion creates a representation for the target API and may allocate additional storage; do not repeat it inside a tight loop without a reason.
Prefer a list for ordinary application collections that grow, shrink or need read-only interfaces. An array does not become the best choice simply because a benchmark mentions primitive storage. Choose from the actual operation and size requirements, then measure only when performance matters. Primitive arrays are particularly helpful when the data is genuinely a numeric buffer rather than a list of domain records.
Bounds, positions and safe access
Indices runs from zero through size minus one. Use indices, withIndex or a direct value loop instead of inventing a range ending at size. A loop through zero..size includes one invalid position. An out-of-bounds access throws at runtime, so validate a position supplied by a visitor or another subsystem before indexing.
GetOrNull is useful when an absent position has a normal meaning. It returns null rather than throwing for an invalid index. Do not use it to hide an internal loop error: a loop that should visit every valid day deserves correct bounds. Missing optional input and a broken algorithm need different handling.
Keep position separate from identity. Index two may mean Wednesday in a documented seven-day buffer, but it is not automatically a stable product identifier. If records have IDs, use a map or store those IDs alongside the values. Reordering an array changes positions, so callers must not mistake a display index for durable identity.
Copies, equality and nested arrays
Assigning one array variable to another shares the same array instance. CopyOf creates a new outer array with copied elements. For primitive arrays that gives independent numeric positions. For arrays of mutable objects, the contained object references remain shared. A copy is not a recursive clone of every reachable value.
Use contentEquals to compare one-dimensional array contents. Ordinary == does not provide list-style structural comparison for arrays. ContentDeepEquals and contentDeepToString are intended for nested arrays. Equal values and the same array instance are different questions, so select a comparison that matches the intended assertion.
A two-dimensional array is an array of row arrays. Rows can have different sizes, so the structure need not be a perfect rectangle. Create each row separately with Array(rows) { IntArray(columns) }; reusing one row instance for several positions makes a change appear in every shared row. That sharing is often an accidental initialization bug.
Version and platform boundaries
These array operations are established standard-library features. The storage explanation specifically describes Kotlin/JVM; other targets have their own implementation details. Array element mutability and the distinction between primitive arrays and Array remain important across targets. The official array reference documents creation, copying, conversions and the appropriate equality operations. Avoid promising that every use of an array is faster than a list, especially when conversion and allocation dominate the work.
Kotlin Arrays and Primitive Arrays Examples
Each example shows the code, the output it produces and what each part does.
Initialize and modify a numeric buffer
The initializer runs for each valid index. Declaring stock with val prevents assigning a different array to that variable, but it does not freeze the elements. The second position changes from four to nine. Printing with joinToString shows values rather than an implementation-oriented array identity string.
fun main() {
val stock = IntArray(3) { index -> (index + 1) * 2 }
stock[1] = 9
println(stock.joinToString(","))
println(stock.size)
println(stock.sum())
}2,9,6
3
17Kotlin Arrays and Primitive Arrays in Real Projects
Worked exercise: preserve the original stock
Record three starting quantities, copy the buffer, and apply one validated adjustment to the copy. The original must remain unchanged because it represents the starting snapshot. Reject an invalid position and any adjustment that would produce negative stock before writing the new value.
fun adjustedStock(original: IntArray, index: Int, change: Int): IntArray {
require(index in original.indices) { "Unknown stock position" }
val result = original.copyOf()
val next = result[index] + change
require(next >= 0) { "Stock cannot be negative" }
result[index] = next
return result
}
fun main() {
val starting = intArrayOf(4, 7, 2)
val updated = adjustedStock(starting, 1, -3)
println(starting.joinToString(","))
println(updated.joinToString(","))
}4,7,2
4,4,2The exercise uses small bounded integers. A production inventory service also needs overflow limits, persistence and concurrency control; this in-memory copy does not coordinate simultaneous orders. Try changing the first or last valid position and confirm the snapshot still stays unchanged. Test an invalid index in a separate failure case rather than altering the successful output.
Kotlin Arrays and Primitive Arrays FAQ
Does val make an array immutable?
No. Val protects the variable binding, so you cannot replace that variable with a different array. The array's positions remain writable. This distinction matters when you hand the array to another function: that function can change elements it can access. If callers need a stable snapshot, copy appropriately or expose a deliberately read-only representation. Objects contained in a copied array can still share mutable state, so think about both the outer structure and its elements.
When should I choose IntArray instead of a list?
Choose IntArray when a fixed-size integer buffer, Java primitive-array API or measured numeric-storage requirement fits the task. A list is usually easier for everyday collections that change size or carry domain records. IntArray does not provide an immutable variant, and converting it repeatedly to satisfy list APIs can erase the expected benefit. Start with the required operations and ownership policy; use a representation that makes them straightforward before considering optimization.
Why does array equality differ from list equality?
Kotlin arrays follow their array equality behavior rather than the structural equality contract of List. Two separately created arrays can contain the same values while ordinary equality does not report them as equal. ContentEquals asks the explicit content question, and contentDeepEquals extends that idea to nested arrays. Keeping the comparison explicit also documents what a test intends to prove: equal elements, equal nested elements or the exact same storage instance.
Can I add another element to a fixed-size array?
You can create a new larger array with operations such as copyOf or plus, then retain that new array. The original storage does not gain a position in place. If growth happens regularly, a mutable list expresses that requirement more naturally and avoids manual resizing logic. Also remember that a variable declared val cannot be reassigned to the new array, although the expression can be returned or stored under another name.
Kotlin Arrays and Primitive Arrays Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
A matrix must have independent mutable rows. Which initializer fits?
Kotlin Arrays and Primitive Arrays Quiz
2 questions to check what you learned.
What does val protect in a val IntArray variable?
- Every element from any mutation
- Reassignment of the variable binding
- The array from being read
Which operation compares one-dimensional array contents?
- contentEquals()
- Ordinary == as a guaranteed list-style comparison
- size.toString() alone