HashTable源码分析

本文详细剖析了Java中的HashTable,介绍了其构造函数、Put、扩容及Get、Remove操作的内部逻辑。HashTable初始容量11,加载因子0.75,确保线程安全。在Put操作中,通过key计算下标并插入链表;扩容时,按原长度2倍+1进行,并迁移链表元素。Get操作通过下标遍历链表查找匹配key的节点;Remove操作同样依据key进行删除。
摘要由CSDN通过智能技术生成

 

目录

HashTable

HashTable构造函数

HashTable Put分析

HashTable扩容分析

HashTable Get分析

HashTable Remove分析


HashTable

初始容量是11,加载因子是0.75,是线程安全的HashMap,底层采用数组加链表(没有红黑树),每个方法都加了synchronized保证线程安全

HashTable构造函数

在构造时候就开辟了一个长度为11的数组空间,加载因子设置为0.75,预警节点个数设置为11*0.75
public Hashtable() {
    this(11, 0.75f);
}

public Hashtable(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal Load: "+loadFactor);

    if (initialCapacity==0)
        initialCapacity = 1;
    this.loadFactor = loadFactor;
    table = new Entry<?,?>[initialCapacity];
    threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
}

HashTable Put分析

将key和数组长度相&,求得下标,如果数组下标里面没有元素,则直接将新节点存放,如果数组下标里面已经存放了元素,此时

从下标里的元素开始寻找以它为头结点的整个链表,比较key,有相等的就覆盖,没有相等的则将新添加的节点放入刚才计算的下标位置里面,后面链上以该下标位置之前存放的元素为头结点的链表。

下标确定

public synchronized V put(K key, V value) {
    // Make sure the value is not null
    if (value == null) {
        throw new NullPointerException();
    }

    // Makes sure the key is not already in the hashtable.
    Entry<?,?> tab[] = table;
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;//下标确定是,key的hash值对数组长度求余
    @SuppressWarnings("unchecked")
    Entry<K,V> entry = (Entry<K,V>)tab[index];
    for(; entry != null ; entry = entry.next) {//如果确定的下标里面已经存放了元素,则通过equals比较key,相等就覆盖,
//不相等继续与通过next得到的元素比较key,直到找到一个相等的key,覆盖里面的value退出,或者整个链中找不到相等的,就链接到最后
        if ((entry.hash == hash) && entry.key.equals(key)) {
            V old = entry.value;
            entry.value = value;
            return old;
        }
    }
   addEntry(hash, key, value, index);
    return null;
}
private void addEntry(int hash, K key, V value, int index) {
    modCount++;

    Entry<?,?> tab[] = table;
    if (count >= threshold) {  //HashTabel数组中元素个数达到预警节点个数,就扩容
        rehash();

        tab = table;
        hash = key.hashCode();
        index = (hash & 0x7FFFFFFF) % tab.length;
    }

    // Creates the new entry.
    @SuppressWarnings("unchecked")
    Entry<K,V> e = (Entry<K,V>) tab[index];
    tab[index] = new Entry<>(hash, key, value, e);//每次将新添加的元素放在一个链表的头部,
    //如果e为null,代表table[index]里面没有存放元素
    //如果e不为null,代表table[index]里面存放了元素,并且要添加的key与e为开头的链表里面每一个节点的key都不相等
    //此时把新添加的的节点作为头节点后面链接上以e为头结点的链表。
    count++;
}

HashTable扩容分析

按原数组长度2倍+1扩容,预警节点个数:新长度*加载因子,扩容时旧数组元素的移动:

按下标循环旧数组,通过hash值和新数组长度求模求得新下标,将旧元素直接放到新下标里面,再循环以此旧元素为头结点的链表,拿到一个就放到刚才头节点放入的新下标里面,后面链接上以上次放到新下表标里面元素为头节点的链表

每次拿到的节点都作为最新头结点,后面链接上上次头节点为头的链表

protected void rehash() {
    int oldCapacity = table.length;
    Entry<?,?>[] oldMap = table;

    // overflow-conscious code
    int newCapacity = (oldCapacity << 1) + 1;//按2倍加1扩容
    if (newCapacity - MAX_ARRAY_SIZE > 0) {
        if (oldCapacity == MAX_ARRAY_SIZE)
            // Keep running with MAX_ARRAY_SIZE buckets
            return;
        newCapacity = MAX_ARRAY_SIZE;
    }
    Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];

    modCount++;
    threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);//预警节点=扩容后的长度*加载因子(0.75)
    table = newMap;

    for (int i = oldCapacity ; i-- > 0 ;) {//旧数组中的元素移位
        for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
            Entry<K,V> e = old;
            old = old.next;

            int index = (e.hash & 0x7FFFFFFF) % newCapacity;//计算新下标
            e.next = (Entry<K,V>)newMap[index];
            newMap[index] = e;//每次拿到的节点都作为最新头结点,后面链接上上次头节点为头的链表
        }
    }
}

HashTable Get分析


通过key计算下标,循环以计算出的下标位置为头结点的链表,依次比较key,相等则返回此节点

public synchronized V get(Object key) {
    Entry<?,?> tab[] = table;
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
        if ((e.hash == hash) && e.key.equals(key)) {
            return (V)e.value;
        }
    }
    return null;
}

HashTable Remove分析

public synchronized V remove(Object key) {
    Entry<?,?> tab[] = table;
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    @SuppressWarnings("unchecked")
    Entry<K,V> e = (Entry<K,V>)tab[index];
    for(Entry<K,V> prev = null ; e != null ; prev = e, e = e.next) {
        if ((e.hash == hash) && e.key.equals(key)) {//定位到要删除的元素
            modCount++;
            if (prev != null) {//删除的元素非链表头结点,则将要删除的节点的上一个节点链接上要删除的节点的后一个节点
                prev.next = e.next;
            } else {//删除的元素是链表头结点,则数组下标中直接存放上要删除节点的下一个节点
                tab[index] = e.next;
            }
            count--;
            V oldValue = e.value;
            e.value = null;
            return oldValue;
        }
    }
    return null;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值