Может ли дата класс быть ключом в хэшмапе
Что проверяют
Знаете ли вы, что data class уже генерирует equals() и hashCode(), и понимаете ли, из каких полей они считаются. Следом почти всегда спрашивают: что будет, если после put поменять поле ключа.
Как отвечать
Да, может, и это обычная практика: составной ключ кеша, ключ для groupBy, ключ в remember в Compose. Компилятор сам генерирует equals(), hashCode(), toString(), copy() и componentN(). Дальше важны детали:
- только первичный конструктор: в
equals()иhashCode()участвуют лишь свойства из первичного конструктора. Свойство, объявленное в теле класса, на равенство не влияет; - неизменяемость:
HashMapраскладывает ключи по корзинам поhashCode()в момент вставки. Если ключ содержитvarи вы его поменяли,get()вернётnull, а запись останется в старой корзине до пересоздания карты. Поэтому ключ собирают изvalи неизменяемых типов; - вложенные типы: хеш data class складывается из хешей полей. Поле типа
Arrayсравнивается по ссылке, и два одинаковых по содержимому ключа окажутся разными. БеритеListили переопределите методы черезcontentEquals()иcontentHashCode(); - ручная реализация: если вы сами написали
equals()илиhashCode()в теле, компилятор возьмёт вашу версию. Переопределять нужно оба метода согласованно.
Про саму карту полезно добавить: в Kotlin на JVM HashMap это java.util.HashMap, и начиная с Java 8 длинная корзина при коллизиях превращается в дерево. Плохой хеш бьёт по скорости, но не по корректности.
Где спотыкаются
Говорят, что для работы в HashMap нужно вручную переопределить equals() и hashCode(). Для data class это уже сделано, вопрос как раз в том, из чего они собраны.
Кладут в ключ изменяемую модель, например сущность Room с полями var, а потом правят её. Карта не падает, она просто перестаёт находить значение, и такую ошибку ищут долго.
Забывают про свойства в теле класса: два объекта с разным значением такого поля считаются одним ключом, и значение в карте молча перезаписывается.