An array in Kotlin holds a fixed number of values of one type, stored next to each other in memory and read by index. You set the length when you create it. After that, it doesn’t change.
Most of the choice between an array and a List or MutableList comes down to that one rule. JetBrains builds the array type into the compiler itself, so the same type works on JVM, Android, Kotlin/Native, and Kotlin/JS.
The specialized primitive classes are older than people think. Kotlin’s API reference lists every one of them, from IntArray to BooleanArray, as part of the standard library since version 1.0. JetBrains made that release stable in February 2016.
What Is a Kotlin Array?

The size is set once, at creation, and it stays the same for as long as the object exists. You can swap values in and out of the slots as often as you like. You can’t add a slot.
When Kotlin compiles, the array becomes whatever the target platform uses natively. That means a JVM array in bytecode, a native array in a Kotlin/Native binary, or a JavaScript array on the web.
JetBrains built Kotlin as a statically typed alternative to Java, and that JVM background shows in how arrays behave.
Indexing starts at zero. The type comes from the Array class, with a set of specialized primitive versions next to it.
People coming from C often miss this: a Kotlin array isn’t a raw block of memory. It’s an object, and it has properties like size and functions like sort() attached to it.
What Types of Arrays Does Kotlin Support?

Most of the time you’ll use one of the primitive-backed classes. Everything else goes into the generic Array<T>.
The primitive versions exist to save memory. Without them, every Int would get wrapped in its own object.
Primitive Array Classes
Each basic type has its own class:
- IntArray
- ByteArray
- ShortArray
- LongArray
- FloatArray
- DoubleArray
- BooleanArray
- CharArray
On the JVM, each of these compiles to the matching raw primitive array, with no boxing and no wrapper objects.
The gap is bigger than most people guess. Marcin Moskala measured it with 1 million integers in Effective Kotlin (Kt. Academy):
- IntArray allocates 4,000,016 bytes. List<Int> takes 20,000,040 bytes for the same data, a 5x difference.
- Averaging those 1 million values ran about 25% faster on IntArray than on List<Int>.
The static analysis tool detekt has flagged Array<Int>-style parameters by default since version 1.2.0, specifically to push developers back toward the primitive classes. I keep that rule on.
The Generic Array Class
The type parameter in Array<T> can be any Kotlin type, including nullable types.
On the JVM you pay for that flexibility. Every value gets boxed into a wrapper object instead of being stored as a raw primitive.
So I only use it when nothing else fits. That usually means an array of strings or custom objects, one that has to hold nulls, or generic code. For numbers, the primitive classes are the better choice nearly every time.
How Do You Create a Kotlin Array?

It depends on what you have when you write the line. Whichever function you use, the compiled result is the same underlying array type.
If you already know every value, arrayOf(1, 2, 3) is the shortest option and the one you’ll see most. arrayOf() is built on a vararg parameter, which is why it takes any number of arguments without a stack of hand-written overloads.
When the values follow a pattern, Array(size) { init } runs the lambda once for each index and fills the slots with what it returns.
IntArray(size) and the other primitive constructors give you a zero-filled array of a set length. That’s handy for buffers.
arrayOfNulls() gives you nullable placeholders, and emptyArray() gives you a zero-length starting point. I rarely use emptyArray().
Primitive and generic constructors use slightly different syntax, and it’s easy to mix them up. Keep a kotlin cheat sheet open while you try them.
How Do You Access and Modify Elements in a Kotlin Array?

You use square brackets, like most C-family languages. array[0] reads the first element, and array[0] = 5 overwrites it.
Counting starts at zero, so the last valid position is always one less than the size. Kotlin has lastIndex for exactly that, so you don’t have to write the subtraction yourself.
The size property is set at creation and never updates. indices returns a range covering every valid index, and I use it in loops more than anything else here.
If you read or write past the end, Kotlin throws ArrayIndexOutOfBoundsException at runtime. Kotlin’s own array reference documents this.
The values in the slots can change, but the number of slots can’t.
Kotlin Array vs List: What Is the Difference?

Size is the obvious difference. The one people miss is interfaces. An array never implements the Collection interface, while List and MutableList both do, and most of the everyday differences come from that.
| Type | Resizing | Interfaces Implemented | Typical Use |
|---|---|---|---|
| Array | Fixed at creation | None of the collection interfaces | Java interop, primitive-heavy code |
| List | Read-only view, size fixed | Collection, Iterable | General application code |
| MutableList | Grows and shrinks freely | Collection, MutableCollection | Data that changes over time |
| ArrayList | Grows and shrinks freely | MutableList, RandomAccess | Default resizable implementation |
Array Type Variance Compared to List Variance
Kotlin’s language documentation says arrays are invariant. Array<String> is not treated as a subtype of Array<Any>, even though String is a subtype of Any.
List declares its type parameter with out, so you can pass a List<String> wherever a List<Any> is expected.
For arrays, the workaround is a type projection, written Array<out Any>. A function with that parameter accepts arrays of any subtype without risking a type failure at runtime.
That’s why generic array parameters look a bit odd next to generic list parameters in Kotlin code.
How Do Multidimensional Arrays Work in Kotlin?

Kotlin has no special type for them. You make an array whose elements are also arrays, and that’s your grid.
Creating a Jagged Array
For a rectangular grid, Array(rows) { IntArray(cols) } creates one inner array per row, and they all have the same length.
Kotlin doesn’t require equal lengths, though. Each inner array can have a different length, and that’s what a jagged array is. It’s useful for triangle-shaped data, where padding every row would waste memory.
To read a value you index twice, as in grid[row][col]. The first index picks the inner array and the second picks the element in it.
How Does a Kotlin Array Work Across the JVM, Kotlin/Native, and Kotlin/JS?
The source code is the same for every target, but each platform compiles it to a different native representation. The split between primitive and generic arrays is visible in JVM bytecode.
JVM Representation and Java Interop
IntArray becomes int[], and ByteArray becomes byte[]. There’s no object layer between them.
Array<Int> compiles to Integer[] instead. Baeldung’s bytecode analysis shows the JVM inserting an Integer.valueOf() call every time a value is stored, boxing it on the spot.
Kotlin was designed to run on the JVM next to existing Java code, so this interop layer matters. When a team chooses between Kotlin or Java for a project, details like this often decide it.
Kotlin/Native and Kotlin/JS Representation
Neither one has the JVM’s boxing behavior.
Kotlin/Native has no JVM underneath, so there’s no boxing in the Java sense. The specialized array classes still exist so the API stays consistent across platforms.
Kotlin/JS is less tidy. Array<T> becomes a standard JavaScript array. IntArray and the other primitive arrays have their own JavaScript typed-array counterparts, such as Int32Array, and dedicated conversion functions move data between the two. So primitive and generic arrays aren’t represented the same way at the JS interop layer.
Both targets fall under Kotlin Multiplatform, JetBrains’ framework for sharing one codebase across JVM, native, and web targets.
Performance changes from one target to another. Profile on the runtime you actually ship to instead of assuming the JVM numbers carry over.
When Should You Use a Kotlin Array Instead of a List?

Use an array when the size is known and fixed, and the code is performance-sensitive or does a lot of interop. Otherwise, List or MutableList is almost always the better default.
These are the cases where I actually use one:
- Fixed-size numeric or byte data, where the boxing overhead of a generic collection shows up in profiling
- Functions with a vararg parameter (the compiler represents that parameter as an array internally, so you’re already using one)
- Calls into a Java based API that expects a native array type rather than a List
- Data that was never going to resize anyway. In that case the fixed length is a guarantee, not a limitation.
Android is where you’ll run into this most. According to the official Android developer reference, Android’s Bundle class only accepts native arrays for its typed extras. putIntArray() and putStringArray() take int[] and String[], not a List.
That affects a lot of real-world Android development code. Data that goes through an Intent or a Bundle has to be an array, even if the rest of the app uses lists.
Everywhere else I default to List. I only switch to an array when profiling or a platform API makes me.
What Built-in Functions Does Kotlin Provide for Working with Arrays?

The standard library has extension functions for comparing, copying, sorting, and converting every array type. If you’ve used the collection functions, most of these will look familiar.
Comparing and Copying Arrays
contentEquals() compares two arrays element by element. It returns true only when the sizes match and every value matches.
copyOf() duplicates an array. If you ask for a bigger size, it pads the result with null or zero values. copyOfRange() copies a slice instead, from an inclusive start index to an exclusive end index.
Neither function changes the original array. Both return a new instance.
Using the Spread Operator and Vararg Parameters
The spread operator is an asterisk written before an array name, and it passes the array’s values into a vararg parameter. In most cases, it makes a full copy of the array first.
There’s one exception. Since version 1.1.60, the Kotlin compiler skips the copy when the array literal is built directly inside the function call.
The copy has a structural reason. Kotlin compiles vararg parameters to standard Java varargs, and the JVM needs its own array instance to work with.
Converting Arrays to Lists and Back
toList() copies an array’s values into a List. toTypedArray() does the reverse and turns a List into an Array<T>.
toIntArray(), toByteArray(), and similar functions convert an object-type array back to its primitive form. Only use them when you know every value fits that primitive type and none of them are null.
What Common Errors Occur When Working with Kotlin Arrays?

Most array bugs I’ve seen come from one of two habits. People either treat a fixed-size structure as if it can grow, or they compare arrays as if they were simple values.
Index out of bounds is the classic one. Reading or writing past the last valid position throws ArrayIndexOutOfBoundsException at runtime.
The equality mistake is harder to spot. == and != compare references, not contents, so two arrays with identical values silently return false. JetBrains ships an inspection for this called ReplaceArrayEqualityOpWithArraysEquals. It flags == or != on arrays and offers a one-click fix to contentEquals().
Calling .add() or .remove() on an array won’t compile, because Kotlin’s own documentation confirms arrays don’t support either function.
Nullable elements are the last common problem. If you pass an Array<String?> into Java code without a null check, you risk a NullPointerException on the Java side.
The compiler only catches the add/remove mistake. The rest show up at runtime, so consistent unit testing is how you catch them early.
When Should You Avoid Using a Kotlin Array?

Skip the array if the collection needs to grow or shrink after you create it. Skip it too if covariant typing matters more to you than raw performance. Business logic in the application layer almost always fits one of those two cases.
Arrays work well for fixed-size numeric data, Java interop boundaries, and vararg-backed signatures. They stop fitting once the data needs to change size. Generic code that needs covariance is another weak spot, because Array<T> stays invariant while List<out T> does not.
List handles everyday application code, generic APIs, and anything whose size will change. It only loses in a few narrow, measurable cases where boxing overhead has a real cost in a hot path.
The exception is heavy number crunching over large data sets. That kind of code is still worth the fixed-length tradeoff, even with no Java interop involved.
Anywhere else, an array that can’t resize is either a constraint you chose on purpose or a bug waiting to happen.
FAQ on What Are Kotlin Arrays
What Is the Difference Between a Kotlin Array and a Java Array?
On the JVM they compile to the same thing. The difference is in the type system. Java arrays are covariant and Kotlin arrays are invariant, so Kotlin blocks an unsafe assignment that Java allows unchecked.
Is a Kotlin Array a Collection Type?
No. It implements neither Collection nor Iterable. A for loop still works because arrays declare their own iterator() operator function.
Does a Kotlin Array Support Null Elements?
It depends on the type. Array<String?> can hold null because its type parameter is nullable. IntArray, ByteArray, and the other primitive classes can’t, since their underlying types are non-nullable by definition.
Which Array Type Should You Choose for Performance, Primitive or Generic?
If the data is numeric and speed matters, use a primitive class like IntArray or DoubleArray. Generic Array<T> boxes every value into an object, and primitive arrays avoid that memory overhead completely.
Is a Kotlin Array Faster Than a List?
For raw numeric work, yes. Primitive arrays skip the boxing that List<Int> needs, so iteration and math run faster. With the small collections in typical app logic, the difference is usually too small to matter.
How Do You Print or Convert a Kotlin Array to a Readable String?
toString() returns a hash code, not the contents. contentToString() prints the elements, and joinToString() does the same with custom separators.
How Do You Loop Through a Kotlin Array?
for (item in array) works directly. Loop over array.indices if you need the index, or use withIndex() if you need both the index and the value.
When Should You Reconsider a Kotlin Array Choice?
Rethink it when the data is no longer a fixed size, or when a generic, covariance-friendly API fits the job better than a raw array.
First, check that the element count really changes now, or that the code has moved behind a generic, covariant API. Then profile memory and speed before reverting to List, not afterward.
Switching a hot numeric path from IntArray back to a boxed List costs measurable iteration speed. That’s a fair trade when clear code and easy resizing matter more than a few microseconds.
Key-value data raises the same fixed-versus-flexible question, and maps in Kotlin covers it.
- pip is not recognized in VS Code: Why It Happens and How to Fix It - October 1, 2026
- 10 Enterprise Server Virtualization Platforms for Modern Data Centers - October 1, 2026
- MySQL Cheat Sheet - September 30, 2026



