简谈JAVA基础--HashTable

来源:互联网 发布:oracle恢复原来的数据 编辑:程序博客网 时间:2024/06/16 02:03
HashTable与HashMap的区别:

HashTable底层数据结构与HashMap相差不多,可以看一下简谈JAVA基础--HashMap

HashTable由于每个方法都通过synchronized关键字来进行修饰,所以说是线程安全的。

而HashMap当多个线程同时操作时,没有同步机制所以会导致数据的不安全。

HashTable键值都不可以为Null。而HashMap的key可以为null。

如果在程序中对HashTable进行类似put( null , null )这样的操作时;编译器是可以通过的,但是会在实际运行时抛出异常。

HashMap的长度为2的幂次方,而HashTable为2的幂次方+1。

所以在扩容操作时,HashMap扩展为原先二倍,HashTable为二倍+1。

基本参数:

Entry<?,?>[] table   -- 存放Entry数组(实际上每个Entry就是一个键值对,当Hash值相同时,以链表形式保存)

count   -- 相当于size长度,代表当前已存在键值对个数

threshold    -- 容量:可最大存储键值对个数

loadFactor    -- 加载因子

initialCapacity    -- 实例化长度。

实例化源码:
public Hashtable(int initialCapacity, float loadFactor) {    // 初始化长度不能小于0    if (initialCapacity < 0)        throw new IllegalArgumentException("Illegal Capacity: "+                initialCapacity);    // 加载因子不能小于等于0,loadFactor不能为非数字    if (loadFactor <= 0 || Float.isNaN(loadFactor))        throw new IllegalArgumentException("Illegal Load: "+loadFactor);    // 如果初始化长度为0,设置为1    if (initialCapacity==0)        initialCapacity = 1;    // 加载因子赋值    this.loadFactor = loadFactor;    // 创建长度为initialCapacity的Entry数组    table = new Entry<?,?>[initialCapacity];    // 容量为最大数 与 初始长度 * 加载因子中较小的一个    threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);}


Put操作源码:
public synchronized V put(K key, V value) {    // Make sure the value is not null    // value不能为空    if (value == null) {        throw new NullPointerException();    }    // 将当前table赋给tab[]    // Makes sure the key is not already in the hashtable.    Entry<?,?> tab[] = table;    // 得到key的hash    int hash = key.hashCode();    // 获得数组中的索引    int index = (hash & 0x7FFFFFFF) % tab.length;    @SuppressWarnings("unchecked")    // 将tab数组中该索引地址内容赋给 entry    Entry<K,V> entry = (Entry<K,V>)tab[index];    // 如果有相同key的存在,覆盖value, return    for(; entry != null ; entry = entry.next) {        if ((entry.hash == hash) && entry.key.equals(key)) {            V old = entry.value;            entry.value = value;            return old;        }    }    // 没有key值存在则添加键值    addEntry(hash, key, value, index);    return null;}


addEntry源码:
private void addEntry(int hash, K key, V value, int index) {    modCount++;    // 原对象table赋给tab[]    Entry<?,?> tab[] = table;    // 如果当前键值对个数大于等于 最大可容纳数量    if (count >= threshold) {        // Rehash the table if the threshold is exceeded        // 进行扩容操作        rehash();        // 扩容后的table赋给tab        tab = table;        // 获取数组索引        hash = key.hashCode();        index = (hash & 0x7FFFFFFF) % tab.length;    }    // Creates the new entry.    // 创建一个Enrty赋给数组索引位置    @SuppressWarnings("unchecked")    Entry<K,V> e = (Entry<K,V>) tab[index];    tab[index] = new Entry<>(hash, key, value, e);    count++;}


rehash(扩容)源码:

protected void rehash() {    // 原数组长度    int oldCapacity = table.length;    // 原数组    Entry<?,?>[] oldMap = table;    // overflow-conscious code    // 新数组长度为原数组长度 * 2 + 1    int newCapacity = (oldCapacity << 1) + 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];    // 操作统计+1    modCount++;    // 新数组可容纳键值对数量    threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);    // 修改当前table引用为新数组    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;        }    }}