MST

星途 面试题库

面试题:Java中Hashtable的put方法在源码层面是如何实现的

请结合Java Hashtable源码,详细阐述put方法在遇到哈希冲突时是如何处理的,以及在扩容方面的策略是什么。
24.4万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

1. put方法处理哈希冲突

在Java的Hashtable中,put方法遇到哈希冲突时采用链地址法(拉链法)来处理。

  1. 计算哈希值:首先通过keyhashCode()方法获取哈希码,然后对哈希表的容量table.length取模得到桶的索引位置index。代码如下:
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
  1. 遍历链表:在找到的桶位置tab[index]处,可能已经存在一个链表(Entry类型的链表)。Entry类包含keyvaluenext等属性。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;
    }
}
  1. 添加新元素:如果遍历完链表都没有找到相同的key,则创建一个新的Entry对象,并将其插入到链表头部(头插法)。代码如下:
Entry<K,V> e = tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;

2. 扩容策略

  1. 扩容条件:当Hashtable中的元素个数count达到阈值threshold时,会触发扩容。阈值的计算公式为threshold = (int)(capacity * loadFactor),其中loadFactor是负载因子,默认值为0.75f。代码如下:
if (count >= threshold) {
    // 扩容操作
}
  1. 扩容方式:扩容时,新的容量为旧容量的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;
    }
}

这样在扩容时,会遍历旧哈希表中的所有元素,重新计算它们在新哈希表中的位置并插入,从而保证哈希表在元素增加的情况下仍能保持较好的性能。