Java中能否将含final键的装饰类作为HashMap键,直接用原键取值?
问题分析:自定义类作为HashMap键,能否直接用内部final字段取值?
你的需求无法通过当前代码实现,调用map.get(3)会返回null,核心原因是违反了equals方法的对称性约定,导致HashMap的查找逻辑失效,具体分析如下:
为什么当前代码不生效?
HashMap的查找流程是:
- 调用传入键的
hashCode()确定哈希桶位置; - 在对应桶中遍历元素,通过
equals()方法比较传入键和桶内的键。
当你调用map.get(3)时:
- 传入的是
Integer类型的3,它的hashCode()返回3,和Decorator的hashCode()返回值一致,所以能定位到正确的哈希桶; - 但接下来HashMap会用
3.equals(decorator实例)做比较,而Integer类的equals()方法只会和同类型对象比较,直接返回false,因此无法匹配到桶内的Decorator实例。
你重写的Decorator.equals(Object)虽然能让decorator实例.equals(3)返回true,但反过来3.equals(decorator实例)是false,破坏了equals方法的对称性(如果a.equals(b)为true,b.equals(a)必须也为true),这是HashMap正常工作的必要前提。
可行的解决方案
方案1:直接用Integer作为HashMap的键
把额外字段包装到值对象中,这样就能直接用Integer取值,逻辑更清晰:
Map<Integer, DecoratorWrapper> map = new HashMap<>(); private static class DecoratorWrapper { private String s; public DecoratorWrapper(String s) { this.s = s; } public String getS() { return s; } public void setS(String s) { this.s = s; } } // 使用示例 map.put(3, new DecoratorWrapper("some string")); // 获取值并访问s字段 System.out.println(map.get(3).getS());
方案2:保留Decorator作为键,简化取值时的实例创建
如果一定要用Decorator做键,可以添加静态工厂方法快速创建仅包含final字段的实例,用于取值:
Map<Decorator, String> map = new HashMap<>(); private static class Decorator { private final int i; private String s; public Decorator(int i, String s) { this.i = i; this.s = s; } // 静态工厂方法,快速创建用于查找的实例 public static Decorator forLookup(int i) { return new Decorator(i, null); // s字段不影响哈希和equals,可传默认值 } public String getS() { return s; } public void setS(String s) { this.s = s; } @Override public int hashCode() { return i; } @Override public boolean equals(Object obj) { if (!(obj instanceof Decorator)) { return false; } Decorator other = (Decorator) obj; return i == other.i; } } // 使用示例 map.put(new Decorator(3, "some string"), "some value"); // 用工厂方法创建实例取值 System.out.println(map.get(Decorator.forLookup(3)));
内容的提问来源于stack exchange,提问作者Dani
相关产品推荐
相关产品推荐

