对这个相当天真的问题道歉,但我相信我自己的回答是天真的。我认为密钥(在HashTables中)是不可变的,因为我们不希望以某种方式意外地改变密钥,因此混乱了HashTable的排序。这是正确的解释吗?如果是这样,它怎么可能更正确?
答案 0 :(得分:5)
在HashTable.put
期间对密钥进行哈希处理,并将其值存储在基于哈希的多个存储桶(键值对列表)之一中,例如:
bucket[key.hashcode() % numberOfBuckets].add(key, value)
如果密钥的hashcode
在插入后发生了更改,那么它可能会在错误的存储桶中,然后您就无法找到它,并且哈希表会错误地返回任何null
{ {1}}用于该密钥。
除此之外:了解哈希表的内部工作原理有助于您了解密钥的高质量get
功能的重要性。由于糟糕的哈希码函数可能导致桶中密钥的分布不良。由于存储桶只是列表,因此会导致大量线性搜索,从而大大降低了哈希表的有效性。例如这个可怕的哈希码函数将所有内容放在一个桶中,所以它实际上只是一个列表。
hashcode
这也是素数出现在良好哈希码函数中的一个原因,例如:
public int hashcode { return 42; /*terrible hashcode example, don't use!*/ }
答案 1 :(得分:3)
一般的想法是正确的,但不是它的细节。
HashTable中的键不一定是不可变的,它是调用hashCode()
(和equals
)方法的结果,需要保持不可变并且一致(对于哈希表,行为可预测,即)。
从高层次的角度来看,这是因为哈希表的工作方式:当插入(key
,value
)对时,key
内部使用hashCode来找出一个" bucket"价值将放在何处。当value
检索key
时,会再次计算hashCode
,以便找回存储桶。
现在,如果在插入和撤退之间的任何时间点,调用hashCode
的结果发生变化,那么"查找存储桶"将与"插入"不同斗,事情不会出现可预测的行为。
总而言之,给定一个看起来像这样的Key对象(两个内部字符串组成objet,但只有一个,在hashCode / equals中考虑partOfHashCode
):
public static class Key {
private String partOfHashCode;
private String notPartOfHashCode;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((partOfHashCode == null) ? 0 : partOfHashCode.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Key other = (Key) obj;
if (partOfHashCode == null) {
if (other.partOfHashCode != null)
return false;
} else if (!partOfHashCode.equals(other.partOfHashCode))
return false;
return true;
}
}
可以这样使用它:
public static void main(String[] args) {
Map<Key, String> myMap = new HashMap<>();
Key key = new Key();
key.partOfHashCode = "myHash";
myMap.put(key, "value");
key.notPartOfHashCode = "mutation of the key, but not of its hash/equals definition";
System.out.println(myMap.get(key));
}
(这会在控制台中记录&#34;值&#34;对象)。
但以这种方式使用它并不好
public static void main(String[] args) {
Map<Key, String> myMap = new HashMap<>();
Key key = new Key();
key.partOfHashCode = "myHash";
myMap.put(key, "value");
key.partOfHashCode = "mutation of the hashCode of the key";
System.out.println(myMap.get(key));
}
(最后一个例子可以在控制台中记录&#34; null&#34;。
有关此主题的更多信息,您还应该阅读hashCode / equals一致性。
答案 2 :(得分:0)
Java中没有HashTable
- 密钥是不可变的固有保证。甚至不能保证他们的hashcode
保持不变。但是如果添加具有可变hashCode
的密钥,则会遇到麻烦。假设您正在插入一个hashCode
为1的密钥。然后将其插入对应于1的散列桶中。然后将对象更改为hashCode
为2并调用hashMap.get(key)
。当对象仍在hashTable
时,系统将在对应于2的存储桶中查找,但在那里找不到它。您甚至无法remove
该条目,因为它将无法找到。
tl; dr为了使您的应用程序正常工作HashTable
- 密钥需要具有不可变的hashcode
,但您必须自己处理这个事实。