Подтвердить что ты не робот

Метод равенства для класса данных в котлине

У меня есть следующий класс данных

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

Я прочитал, что классы данных в Kotlin получают бесплатный метод equals()/hashcode().

Я создал два объекта.

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))

Но все же следующие утверждения возвращают false.

board1 == board2
board1.equals(board2)
4b9b3361

Ответ 1

В тестах проверки соответствия классов Kotlin data, массивы, как и другие классы, сравниваются с помощью equals(...) , который сравнивает ссылки массивов, а не контент. Это поведение описано здесь:

Итак, всякий раз, когда вы говорите

  • arr1 == arr2

  • DataClass(arr1) == DataClass(arr2)

  • ...

вы получаете массивы, сравниваемые по equals(), то есть по ссылке.

Учитывая, что

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


Чтобы переопределить это и сопоставить структурные массивы, вы можете реализовать equals(...) + hashCode() в своем классе данных, используя Arrays.equals(...) и 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)
}

Этот код - это то, что IntelliJ IDEA может автоматически генерировать для не-данных классов.

Другим решением является использование List<Int> вместо IntArray. Списки сопоставляются структурно, поэтому вам не нужно переопределять что-либо.

Ответ 2

Для классы данных в методе Kotlin метод hashcode() будет генерировать и возвращать одно и то же целое число, если значения параметров одинаковы для обоих объектов.

val user = User("Alex", 1)
val secondUser = User("Alex", 1)
val thirdUser = User("Max", 2)

println(user.hashCode().equals(secondUser.hashCode()))
println(user.hashCode().equals(thirdUser.hashCode()))

Запуск этого кода вернет True и False, как если бы мы создали объект secondUser, мы передали тот же аргумент, что и объект пользователь, поэтому целое число hashCode(), сгенерированное для обоих из них, будет таким же.

также, если вы проверите это:

println(user.equals(thirdUser))

Он вернет false.

В соответствии с методом hashCode() docs

open fun hashCode(): Int (source)

Возвращает значение хэш-кода для объекта. Общий договор hashCode:

Всякий раз, когда он вызывается одним и тем же объектом более одного раза, hashCode метод должен последовательно возвращать одно и то же целое число, если нет информация, используемая при равных сравнениях с объектом, изменяется.

Если два объекта равны в соответствии с методом equals(), то вызов метод hashCode на каждом из двух объектов должен целочисленный результат.

Подробнее см. в этом обсуждении здесь

Ответ 3

Поздний ответ, но он может помочь кому-то, кто пришел к этому вопросу из поиска:

В Kotlin equals() ведет себя по-разному между List и Array, как вы можете видеть из кода ниже:

val list1 = listOf(1, 2, 3)
val list2 = listOf(1, 2, 3)

val array1 = arrayOf(1, 2, 3)
val array2 = arrayOf(1, 2, 3)

//Side note: using a==b is the same as a.equals(b)

val areListsEqual = list1 == list2// true
val areArraysEqual = array1 == array2// false

List.equals() проверяет, имеют ли два списка одинаковый размер и содержат ли они одинаковые элементы в одинаковом порядке.

Array.equals() просто выполняет проверку ссылки на экземпляр. Поскольку мы создали два массива, они указывают на разные объекты в памяти, поэтому они не считаются равными.

Начиная с Kotlin 1.1, для достижения того же поведения, что и с List, вы можете использовать Array.contentEquals().

Источник: Array.contentEquals() docs ; List.equals() документы