使用HashMap和自定义键
快速问题:如果我想使用自定义类作为键的HashMap
,我必须覆盖hashCode
函数吗? 如果我不覆盖该function,它将如何工作?
从技术上讲,只要相等的对象具有相同的hashCode,您就不必重写hashCode方法。
因此,如果您使用Object定义的默认行为,其中equals仅对同一实例返回true,那么您不必重写hashCode方法。
但是如果你不覆盖equals和hashCode方法,那就意味着你必须确保你总是使用相同的密钥实例。
例如:
MyKey key1_1 = new MyKey("key1"); myMap.put(key1_1,someValue); // OK someValue = myMap.get(key1_1); // returns the correct value, since the same key instance has been used; MyKey key1_2 = new MaKey("key1"); // different key instance someValue = myMap.get(key1_2); // returns null, because key1_2 has a different hashCode than key1_1 and key1_1.equals(key1_2) == false
实际上,您通常只有一个键实例,因此从技术上讲,您不必重写equals和hashCode方法。
但最佳做法是覆盖用作键的类的equals和hashCode方法,因为有时候您或其他开发人员可能会忘记必须使用相同的实例,这可能导致难以跟踪问题。
请注意:即使您覆盖equals和hashCode方法,也必须确保不以更改equals或hashCode方法结果的方式更改密钥对象,否则映射将找不到您的值了。 这就是为什么建议尽可能使用不可变对象作为键。
如果不覆盖hashCode AND equals,则将获得默认行为,即每个对象都不同,无论其内容如何。
您不必覆盖hashCode()
函数的唯一时间是您也不重写equals
,因此您使用引用相等的默认Object.equals
定义。 这可能是也可能不是你想要的 – 特别是,即使它们具有相同的字段值,也不会认为不同的对象是相等的。
如果你覆盖equals
但不是hashCode
,那么HashMap
行为将是未定义的(读取:它根本没有任何意义,并且将完全被破坏)。
它取决于您用作键的对象类。 如果它是你提出的自定义类,并且它没有扩展任何东西(即它扩展了Object
),那么hashCode函数将是Object
函数,并且将考虑内存引用,使得两个看起来相同的对象哈希到不同的代码。
所以,是的,除非你使用hashCode()
函数扩展一个类,你知道它适用于你,你需要实现自己的。 还要确保实现equals()
:像ArrayList
这样的类只会使用equals,而像HashMap这样的其他类会检查hashCode()
和equals()
。
还要考虑如果你的密钥不是不可变的,你可能会遇到问题。 如果你在地图中放入一个带有可变键的条目,你稍后会以一种影响哈希码的方式更改密钥,等于你可能会丢失你在地图中的条目,因为你将无法再检索它。
您应该重写Object类中的equals()
和hashCode()
方法。 equals()
和hashcode()
的默认实现equals()
inheritance自java.lang.Object
使用对象实例的内存位置(例如MyObject@6c60f2ea
)。 当一个对象的两个实例具有相同的属性但是inheritance的equals()
将返回false
时,这可能会导致问题,因为它使用的memory location
对于两个实例是不同的。
此外,可以重写toString()
方法以提供对象的正确字符串表示forms。
实现用户定义键时的主要考虑因素
- 如果一个类重写
equals()
,它必须覆盖hashCode()
。 - 如果2个对象相等,那么它们的
hashCode
值也必须相等。 - 如果在
equals()
没有使用某个字段,则不能在hashCode()
使用它。 - 如果经常访问它,
hashCode()
是缓存的候选者以增强性能。