Equals method for data class in Kotlin

Vaibhav picture Vaibhav · May 30, 2016 · Viewed 38k times · Source

I have the following data class

data class PuzzleBoard(val board: IntArray) {
    val dimension by lazy { Math.sqrt(board.size.toDouble()).toInt() }
}

I read that data classes in Kotlin get equals()/hashcode() method for free.

I instantiated two objects.

val board1 = PuzzleBoard(intArrayOf(1,2,3,4,5,6,7,8,0))
val board2 = PuzzleBoard(intArrayOf(1,2,3,4,5,6,7,8,0))

But still, the following statements return false.

board1 == board2
board1.equals(board2)

Answer

hotkey picture hotkey · May 30, 2016

In Kotlin data classes equality check, arrays, just like other classes, are compared using equals(...), which compares the arrays references, not the content. This behavior is described here:

So, whenever you say

  • arr1 == arr2

  • DataClass(arr1) == DataClass(arr2)

  • ...

you get the arrays compared through equals(), i.e. referentially.

Given that,

val arr1 = intArrayOf(1, 2, 3)
val arr2 = intArrayOf(1, 2, 3)

println(arr1 == arr2) // false is expected here
println(PuzzleBoard(arr1) == PuzzleBoard(arr2)) // false too


To override this and have the arrays compared structurally, you can implement equals(...)+hashCode() in your data class using Arrays.equals(...) and Arrays.hashCode(...):

override fun equals(other: Any?): Boolean{
    if (this === other) return true
    if (other?.javaClass != javaClass) return false

    other as PuzzleBoard

    if (!Arrays.equals(board, other.board)) return false

    return true
}

override fun hashCode(): Int{
    return Arrays.hashCode(board)
}

This code is what IntelliJ IDEA can automatically generate for non-data classes.

Another solution is to use List<Int> instead of IntArray. Lists are compared structurally, so that you won't need to override anything.