Kotlin中为何equals返回true,LinkedHashMap却找不到对应值?
问题:URI equals返回true但Map无法找到对应值
我有一个映射:
private val secondaryOutputProperties = mutableMapOf<URI, MutableMap<QName, XdmValue>>()
还有一个来自另一映射键的uri: URI对象。执行以下代码时出现无法解释的空指针异常:
for ((puri, _) in secondaryOutputProperties) { println("Map URI: $puri, ${puri.equals(uri)}") } println("Key URI: $uri") val x = secondaryOutputProperties[uri] println("s[uri] = ${x}") val y = secondaryOutputProperties.get(uri) println("s.get(uri) = ${y}") println("in? ${uri in secondaryOutputProperties}") println("conains? ${secondaryOutputProperties.containsKey(uri)}")
输出结果:
Map URI: file:/Volumes/Projects/xproc/test-suite/test-suite/tests/secondary-false, true Map URI: file:/Volumes/Projects/xproc/test-suite/test-suite/tests/secondary-true, false Key URI: file:/Volumes/Projects/xproc/test-suite/test-suite/tests/secondary-false s[uri] = null s.get(uri) = null in? false conains? false
执行val serprops = secondaryOutputProperties[uri]!!触发NPE,程序为单线程,无其他线程修改映射。为什么puri.equals(uri)返回true,但get()方法找不到对应值?
原因分析
这是因为Java/Kotlin默认的Map实现(比如mutableMapOf创建的HashMap)依赖**equals()和hashCode()方法的一致性约定**:如果两个对象equals()返回true,它们的hashCode()必须返回相同的值。你的场景就是这两个方法违反了这个约定。
具体来说:
- HashMap查找键时,先通过
hashCode()定位对应的哈希桶,再在桶内用equals()比较对象。如果两个对象equals()为true但hashCode()不同,HashMap会认为这是两个不同的键,自然找不到对应值。 - 大概率是你使用的
URI对象(比如自定义实现类,或存在特殊属性的标准URI)没有遵循这个约定:要么equals()逻辑没有覆盖所有影响哈希的属性,要么hashCode()没有基于equals()中比较的属性计算。
验证方法
在打印代码中添加哈希值输出,确认两个URI的哈希值是否不同:
for ((puri, _) in secondaryOutputProperties) { println("Map URI: $puri, equals: ${puri.equals(uri)}, hashCode: ${puri.hashCode()}") } println("Key URI: $uri, hashCode: ${uri.hashCode()}")
如果输出的哈希值不一致,就坐实了这个问题。
解决方案
- 使用标准
java.net.URI类:标准URI已经正确重写了equals()和hashCode(),只要两个URI的协议、主机、路径等核心属性一致,这两个方法的结果会保持统一。 - 修复自定义URI类:如果是自定义的URI实现,必须基于
equals()中比较的所有属性来重写hashCode(),确保equals()为true时哈希值相同。 - 临时替换Map实现:如果无法修改URI类,可以改用
TreeMap,它依赖compareTo()(或自定义Comparator)而非hashCode(),但TreeMap的查找性能低于HashMap,仅适合小数据量场景。
内容的提问来源于stack exchange,提问作者Norm
相关产品推荐
相关产品推荐

