您在示例中观察到的是不是碰撞效果。这是正常的元素替换。
在 100 次迭代之后,Hashtable 中只有 10 个元素。
您使用数字 i%10 (0,1,...,9) 作为键。因此,您只有 10 个不同的键。
例如:在您的 for 循环中,您为 key=5 (i=5, i=15, i=95) 放置 10 个值,每个 put(5, val) 替换与 key=5 关联的旧值。
碰撞列表是不同的概念。
对于每个键哈希表计算一些哈希值并使用该哈希在其内部桶表中选择索引。接下来将 {key,value} 放在该索引下。
冲突是指 2 个不同的键计算了相同的桶索引的情况。
例如:
table index | map.entry
0 | {0, "A"}
1 | {3, "B"}
2 | {2, "A"} -> {4, "C"}
3 | {1, "D"} -> {5, "A} -> {6, "F}
在这个例子中,你有一个带有 4 元素内表的哈希表。
此哈希表包含 7 个元素(7 个不同的键)但是:
键 2 和 3 被放置在同一个桶中(它们具有根据哈希值计算的相同索引)
密钥 1、5、6 被放置在同一个存储桶中。
所以我们可以说,key=2 和 key=3 之间以及 1,5,6 之间存在冲突。
换句话说,键 2 和 3 在同一个冲突列表中。键 1、5、6 相同。
您无法从 Hashtable 中获取此类冲突列表,因为它是 Hashtable 内部实现标记为私有:
/**
* Hashtable bucket collision list entry
*/
private static class Entry<K,V> implements Map.Entry<K,V> {
int hash;
final K key;
V value;
Entry<K,V> next;
protected Entry(int hash, K key, V value, Entry<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
...
public V setValue(V value) {
if (value == null)
throw new NullPointerException();
V oldValue = this.value;
this.value = value;
return oldValue;
}
...
public int hashCode() {
return hash ^ value.hashCode();
}
...
}
而Hashtable的内部桶表定义为:
/**
* The hash table data.
*/
private transient Entry<K,V>[] table;
希望这有助于找出哈希表的行为。