Why does == on two arrays with equal contents return false, and how do you compare arrays correctly?
answer
- == on arrays = identity, not contents
- contentEquals / contentDeepEquals for value compare
- contentHashCode for hash keys
- contentToString for readable output
- array in data class = override equals/hashCode (or use List)
basics
~10 sFor arrays, == checks identity (same object), not contents, so two different arrays with the same values are not equal. Use contentEquals() to compare values, and contentHashCode()/contentToString() for hashing and printing.
solid answer
~30 sArrays inherit the default Any.equals (reference identity), so == / equals compares whether the two references point to the SAME array object, not whether their elements match. To compare elements use a.contentEquals(b); for nested arrays use a.contentDeepEquals(b). Likewise a.hashCode() is identity-based, so use a.contentHashCode() (or contentDeepHashCode()) when an array participates in a hash key, and a.contentToString() / contentDeepToString() for readable output instead of the default [I@1b6d3586. A direct consequence: data classes with an array property get a broken equals/hashCode and must override them using the content* functions. This is the single most common array bug in Kotlin.
code
kotlin · 10 linesval a = intArrayOf(1, 2, 3)
val b = intArrayOf(1, 2, 3)
println(a == b) // false (identity)
println(a.contentEquals(b)) // true (contents)
println(a.contentToString()) // [1, 2, 3]
val nested = arrayOf(intArrayOf(1), intArrayOf(2))
val nested2 = arrayOf(intArrayOf(1), intArrayOf(2))
println(nested.contentEquals(nested2)) // false (inner arrays identity)
println(nested.contentDeepEquals(nested2)) // truego deeper
Recognizes that == on arrays can be surprising and that contentEquals exists.
Explains identity vs structural equality and uses contentEquals/contentToString correctly.
Covers the full content/contentDeep family, the data-class equals/hashCode override, and the contrast with List's structural equality.
Treats it as an API-design smell, prefers List in domain types, and guards against the bug with lint rules or reviews across the codebase.
## The trap For every array type, `==` (which calls `equals`) and `hashCode` use **reference identity** — inherited straight from `Any` — NOT element contents: ```kotlin val a = intArrayOf(1, 2, 3) val b = intArrayOf(1, 2, 3) println(a == b) // false — different objects println(a === b) // false — also identity, same result here println(a == a) // true — same reference ``` This surprises people because `List` *does* compare by contents (`listOf(1,2,3) == listOf(1,2,3)` is `true`). ## The correct tools - **`a.contentEquals(b)`** — structural equality of elements (one level deep). - **`a.contentDeepEquals(b)`** — recursive equality for arrays of arrays. - **`a.contentHashCode()` / `a.contentDeepHashCode()`** — content-based hash, needed if the array is a hash key. - **`a.contentToString()` / `a.contentDeepToString()`** — readable `[1, 2, 3]` instead of the default `[I@1b6d3586`. ```kotlin println(a.contentEquals(b)) // true println(a.contentToString()) // [1, 2, 3] println(a.contentHashCode()) // stable across equal contents ``` ## data class consequence Kotlin's generated `equals`/`hashCode`/`toString` for a `data class` call the **member's own** `equals`/`hashCode`/`toString`. For an array property that means identity comparison — so two logically-equal instances compare unequal and print garbage: ```kotlin data class Packet(val bytes: ByteArray) { override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is Packet) return false return bytes.contentEquals(other.bytes) } override fun hashCode() = bytes.contentHashCode() } ``` The IDE even warns "Array property in data class" precisely because of this. Often the cleaner fix is to use a `List` instead of an array in the data class. ## Why arrays behave this way Arrays are thin wrappers over JVM arrays, which have identity-based `equals`/`hashCode` by spec. Kotlin keeps that and provides the `content*` extension functions as the structural alternative, rather than silently overriding `==`. ## Rule of thumb Any time you'd compare, hash, print, or store arrays by value, reach for the `content*`/`contentDeep*` functions — or use a `List`, which already does structural equality.
- Why does listOf(1,2,3) == listOf(1,2,3) return true but the array version returns false?List implementations override equals to compare elements structurally; arrays inherit Any.equals, which is identity-based.
- What goes wrong with a data class that has an Array property, and how do you fix it?Generated equals/hashCode use identity for the array, so equal instances mismatch. Override both with contentEquals/contentHashCode, or switch the property to a List.
Comparing arrays with == is like asking 'are these two ID badges the same physical card?' rather than 'do they name the same person?' — contentEquals asks the second question.
saying these in an interview costs you the question
- Claiming == compares array contents
- Using == in tests to assert array contents
- Leaving an Array property in a data class unhandled
- Using default toString() and reading [I@... as corruption
- Forgetting contentDeepEquals for nested arrays