我试图了解Java中的HashTables实现。以下是我的代码:
Hashtable<Integer, String> hTab = new Hashtable<Integer, String>();
hTab.put(1, "A");
hTab.put(1, "B");
hTab.put(2, "C");
hTab.put(3, "D");
Iterator<Map.Entry<Integer, String>> itr = hTab.entrySet().iterator();
Entry<Integer, String> entry;
while(itr.hasNext()){
entry = itr.next();
System.out.println(entry.getValue());
}
当我运行它时,我得到以下输出: d C 乙
这意味着Key = 1发生了冲突;并根据实施:
“只要hashTable发生冲突,就会在对应特定存储桶的linkedList中创建一个新节点,并将EntrySet(Key,Value)对存储为列表中的节点,新值将插入到开头特定桶的清单“。我完全同意这种实施。
但如果这是真的,那么当我尝试从hashTable中检索入口集时,“A”的位置是什么?
同样,我尝试使用以下代码通过实现我自己的HashCode和equals方法来理解这一点。令人惊讶的是,这是完美的,并根据HashTable实现。以下是我的代码:
public class Hash {
private int key;
public Hash(int key){
this.key = key;
}
public int hashCode(){
return key;
}
public boolean equals(Hash o){
return this.key == o.key;
}
}
public class HashTable1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Hashtable<Hash, String> hTab = new Hashtable<Hash, String>();
hTab.put(new Hash(1), "A");
hTab.put(new Hash(1), "B");
hTab.put(new Hash(2), "C");
hTab.put(new Hash(3), "D");
Iterator<Map.Entry<Hash, String>> itr = hTab.entrySet().iterator();
Entry<Hash, String> entry;
while(itr.hasNext()){
entry = itr.next();
System.out.println(entry.getValue());
}
}
}
输出: d C 乙 甲
哪个是完美的。我无法理解Java中HashTable行为的这种模糊性。
@garrytan和@Brian:感谢您的回复。但我仍有一点疑问。
在我的第二个代码中,它工作正常。我创建了两个对象,它们是新的键,因为它们是2个对象,在这种情况下不会发生键碰撞并且它可以正常工作。我同意你的解释。但是,如果在第一组代码中我使用“new Integer(1)”而不是简单地“1”,它仍然不起作用,尽管现在我正在创建2个对象,它们应该是不同的。我通过写下面的简单行来交叉检查:
Integer int1 = new Integer(1);
Integer int2 = new Integer(1);
System.out.println(int1 == int2);
给出“假”。这意味着现在,Key碰撞应该已经解决了。但它仍然无效。这是为什么?
答案 0 :(得分:4)
按设计哈希表并不意味着存储重复的密钥。
我认为你在'哈希碰撞'和'钥匙碰撞'之间混淆了。简而言之,哈希表由一组链表组成(即:桶)。添加新的键值对(KVP)时,它会通过键的哈希值分配到存储桶中。当两个密钥导致相同的散列(因此它们被放入同一个桶中)时会发生'哈希冲突'
良好的散列函数是将密钥均匀分配到多个桶中的函数,从而提高密钥搜索性能。
答案 1 :(得分:1)
第二个示例给出了您想要的行为,因为您的equals实现不正确。
签名是
public boolean equals(Object o) {}
不
public boolean equals(Hash h) {}
所以你创建的是一个哈希冲突,其中两个对象具有相同的哈希码(密钥),但根据equals方法它们不相等(因为你的签名是错误的,它仍然使用==运算符和不是你的this.key == h.key代码)。与密钥冲突相反,其中对象具有相同的hashCode并且也等于,如第一个示例中所示。如果您修复了第二个示例中的代码以实现实际的equals(Object o)
方法,您会看到值中的“A”将再次丢失。
答案 2 :(得分:0)
在第二个示例中,您没有覆盖原始的equals函数,因为您使用以下签名:
public boolean equals(Hash h) {}
因此仍然使用原始的equals函数与Object作为参数,并且当您为每个插入创建一个新对象Hash时,Object与另一个插入不同,因此A和B的键不相等。
此外,HashTable被设计为具有EACH键的一个值。并且密钥确实依赖于要比较的equals函数。
关于两个新整数的示例,请尝试将它们与.equals()进行比较。您还可以覆盖hashCode函数,以便为每个对象生成不同的hashCodes,即取决于时间,但这不是一个好的编码原则。相同的对象应该散列到相同的代码。