使用Map作为Key时Java Map.containsKey()返回false原因解析
Map的containsKey()始终返回false的原因分析
根因
HashMap的键查询逻辑依赖键对象的hashCode()和equals()方法:键存入时会基于当时的hashCode计算它在哈希表中的存储位置,后续查询时会先计算查询对象当前的hashCode,再到对应位置做匹配。- Java中
HashMap的hashCode计算与自身存储的所有键值对强绑定,只要内部键值对发生增删改操作,hashCode就会同步变化。你场景中作为键的内部detailsMap在循环中持续新增元素,它的hashCode每次循环都会更新。 - 首次将
detailsMap作为键存入外层objectDetailsToSize时,存储位置是基于当时的旧hashCode计算得到的;后续detailsMap内容更新后hashCode改变,调用containsKey()时会用新的hashCode查找对应存储桶,自然找不到存在旧位置的同一个键,所以始终返回false。 - 这种可变对象作为HashMap键的用法除了查询失败,还会引发内存泄漏:已经存入的旧键永远无法被正常访问、移除,会一直占用内存。
问题复现代码
import java.util.HashMap; import java.util.Map; import java.util.Random; public class ExampleMap { private static Map<Map<Long,Boolean>, Boolean> objectDetailsToSize= new HashMap<>(); public static void main(String[] args) { Random rn = new Random(); int size = 10; Map<Long,Boolean> detailsMap = new HashMap<>(); for(int i=0; i< size; i++) { detailsMap.put(rn.nextLong(),rn.nextBoolean()); if(!objectDetailsToSize.containsKey(detailsMap)) { objectDetailsToSize.put(detailsMap, rn.nextBoolean()); System.out.println("Why containsKey() method is failing here"); } } } }
修复方案
HashMap要求作为键的对象必须保证hashCode不可变,因此不要直接用可变的HashMap作为键,可改为用内部Map的不可变副本作为键:
// 每次新增元素后生成不可变副本用于键校验 Map<Long,Boolean> immutableKey = Map.copyOf(detailsMap); if(!objectDetailsToSize.containsKey(immutableKey)) { objectDetailsToSize.put(immutableKey, rn.nextBoolean()); }
如果需要兼容低版本Java(Java 10之前没有Map.copyOf),也可以自定义不可变的业务类封装内部Map的属性,重写hashCode和equals方法作为键使用。
内容的提问来源于stack exchange,提问作者Mohasin Ali
相关产品推荐
相关产品推荐

