HashCode Equals 小坑
今天同事在旁边问我,“哎?hashmap 为什么 hashcode 是 0 啊”
我猜:“估计你是个空 map 吧”
果不其然,同事用的是一个空 map,不知道他经历了什么,总不至于把 hashmap 塞到 hashmap 里当 key 了吧?
如此,让我想起了经典又无聊的面试题,hashcode 和 equals。同时,也让我想起了操蛋的 kotlin data class,那我们一一来看下
HashMap.hashCode() 和 equals
hashcode 和 equals 我们直接查看源码 AbstractMap,如下:
public int hashCode() {
int h = 0;
for (Entry<K, V> entry : entrySet())
h += entry.hashCode();
return h;
}
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Map<?, ?> m))
return false;
if (m.size() != size())
return false;
try {
for (Entry<K, V> e : entrySet()) {
K key = e.getKey();
V value = e.getValue();
if (value == null) {
if (!(m.get(key) == null && m.containsKey(key)))
return false;
} else {
if (!value.equals(m.get(key)))
return false;
}
}
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
return true;
}
所以,当 map 没有数据时,hashcode 自然而然也就是 0 了,同时 equals 和 hashcode 判断的都是具体的内容
假如,这个时候你做了一件变态的事情,把 hashmap 当做 hashmap 的 key(当然现实应该没人这么搞),如下:
fun main() {
val map1 = mutableMapOf<String, Any>("key1" to "value1")
val map2 = mutableMapOf<String, Any>("key1" to "value1")
// // 或者为空,或者内容的 hashcode 一致
// val map1 = mutableMapOf<String, Any>("key1" to "value1")
// val map2 = mutableMapOf<String, Any>("key1" to "value1")
val map3 = mutableMapOf<Map<String, Any>, Any>()
map3[map1] = "1"
map3[map2] = "2"
println(map3)
}
最终,这个 map 里都只有一个 item,也很好理解,hashcode 一致,同时,内容一致,也 equals 了,所以,被覆盖了
这个应该正常没人会这么写,如果有这种神奇的场景,那也太···
Data Class
kotlin 提供了 data class 这么一种特殊类型,用时一时爽,用后火葬场
当时,chatgpt 出来的时候,被别人忽悠去当免费劳动力,帮别人写个类似的东西,此时,就用上了 RecyclerView 和 List
List 中的 item 当然就定义成 data class 了
如:
data class CustomData(val data: Any)
后来,忘记了什么需求了,好像是历史消息还是什么之类的,转成了 LinkedHashSet?
总之,我们来看下面这段代码
val custom1 = CustomData("1")
val custom2 = CustomData("1")
val set = linkedSetOf<CustomData>()
set.add(custom1)
set.add(custom2)
println(set)
试问,最终 set 里面有几个元素?答:1 个
因为 data class 默认也帮忙重写了 hashCode & equals 方法,也在比较内容,内容相同,所以只有 1 个元素
同理,在使用 list 的时候, indexOf 有相似的问题,看如下代码
val list = listOf<CustomData>()
list.add(custom1)
list.add(custom2)
list.indexOf(custom2)
此时 indexOf 返回的 index = 0,因为 indexOf 在不为 null 的情况下,也是使用的 equals 比较,而 data class 重写了 equals 比较内容
总结
我们在使用 hashmap 和 判断 equals 时,特别要注意判断的对象的 hashcode 和 equals 方法,特别是 kotlin data class,使用范围很广,很多人忘记了 data class 比较的是具体的内容,而不是对象本身是不是同一个,而会产生一些奇奇怪怪的 bug 现象
这本来是十分简单的问题,但是很多人并没有在意过,导致最终结果不符预期而百思不得其解(类似的还有 json 的反序列化 和 混淆问题,很多工作很久的人完全不注意,导致 release 包出问题,又傻傻找不到原因)