面试题答案
一键面试1. put
方法处理哈希冲突
在Java的Hashtable
中,put
方法遇到哈希冲突时采用链地址法(拉链法)来处理。
- 计算哈希值:首先通过
key
的hashCode()
方法获取哈希码,然后对哈希表的容量table.length
取模得到桶的索引位置index
。代码如下:
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
- 遍历链表:在找到的桶位置
tab[index]
处,可能已经存在一个链表(Entry
类型的链表)。Entry
类包含key
、value
、next
等属性。put
方法会遍历该链表,检查是否存在相同的key
。如果存在相同的key
,则更新其对应的value
。代码如下:
for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
V old = e.value;
e.value = value;
return old;
}
}
- 添加新元素:如果遍历完链表都没有找到相同的
key
,则创建一个新的Entry
对象,并将其插入到链表头部(头插法)。代码如下:
Entry<K,V> e = tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
2. 扩容策略
- 扩容条件:当
Hashtable
中的元素个数count
达到阈值threshold
时,会触发扩容。阈值的计算公式为threshold = (int)(capacity * loadFactor)
,其中loadFactor
是负载因子,默认值为0.75f
。代码如下:
if (count >= threshold) {
// 扩容操作
}
- 扩容方式:扩容时,新的容量为旧容量的2倍再加1。然后重新计算每个元素在新哈希表中的位置,并将元素重新插入到新的哈希表中。代码如下:
Entry<K,V>[] oldTab = tab;
int oldCapacity = oldTab.length;
int newCapacity = oldCapacity * 2 + 1;
Entry<K,V>[] newTab = (Entry<K,V>[])new Entry<?,?>[newCapacity];
threshold = (int)(newCapacity * loadFactor);
tab = newTab;
for (int i = oldCapacity ; i-- > 0 ;) {
for (Entry<K,V> old = oldTab[i] ; old != null ; ) {
Entry<K,V> e = old;
old = old.next;
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = newTab[index];
newTab[index] = e;
}
}
这样在扩容时,会遍历旧哈希表中的所有元素,重新计算它们在新哈希表中的位置并插入,从而保证哈希表在元素增加的情况下仍能保持较好的性能。