java8-HashTable源码分析

文章目录

      • HashTable
        • 1.成员变量
        • 2.构造
        • 3.常用方法-put&get
          • 2.get
        • 4.HashTable与HashMap不同
          • 关于HashTable中不允许key,value值为空

HashTable

public class Hashtable<K,V>	
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable

1.成员变量

//通过Entry数组保存数据
private transient Entry<?,?>[] table;

// 用来统计这个哈希数组已经使用的空间
private transient int count;

// 设置的一个阈值,当超出这个阈值时就要进行扩容
private int threshold;

//加载因子
private float loadFactor;

/* 用来记录hashtable结构更改的次数,此字段用于使哈希表集合视图中的迭代器快速失效。
  This field is used to make iterators on Collection-views of
 * the Hashtable fail-fast.  (See ConcurrentModificationException).
 */
private transient int modCount = 0;	
//能够分配的最大的大小,需要小心越界
// OutOfMemoryError: Requested array size exceeds VM limit
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

相对于HashMap来说,有些不同的地方就是用于存储的对象

HashMap使用Node来存储键值对,而HashTable使用Entry对象来进行存储

HashTable继承了 Dictionary这个类。

2.构造

加载因子和初始容量是影响其性能的两个因素。默认加载因子大小 0.75是时间和空间消耗最小的。

 public Hashtable(int initialCapacity, float loadFactor) {	 
   //initialCapacity是初始容量, loadFactor是加载因子
    if (initialCapacity==0)
        initialCapacity = 1;
    this.loadFactor = loadFactor;
    table = new Entry<?,?>[initialCapacity];
   // The value of this field is (int)(capacity * loadFactor.)  
    threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
}
//使用指定的初始容量和默认的加载因子0.75构建一个新的空哈希表
public Hashtable(int initialCapacity) {
    this(initialCapacity, 0.75f);
}
 // 无参构造  默认初始容量为11,加载因子是0.75  而hashmap默认容量16
public Hashtable() {
    this(11, 0.75f);
}
// 构造一个新的哈希表, 具有与给定的map相同的映射。
public Hashtable(Map<? extends K, ? extends V> t) {
        this(Math.max(2*t.size(), 11), 0.75f);
        putAll(t);
}

Entry结构: 通过entry结构中的next属性可以知道, hashtable的结构是一个单向链表

 private static class Entry<K,V> implements Map.Entry<K,V> {    
	final int hash;//用来记录当前entry的地址
    final K key;
    V value;
    Entry<K,V> next;//表明指向的下一个entry

    protected Entry(int hash, K key, V value, Entry<K,V> next) {
        this.hash = hash;
        this.key =  key;
        this.value = value;
        this.next = next;
    }

    @SuppressWarnings("unchecked")
    protected Object clone() {
        return new Entry<>(hash, key, value,
            (next==null ? null : (Entry<K,V>) next.clone()));
    }

    // Map.Entry Ops

    public K getKey() {
        return key;
    }

    public V getValue() {
        return value;
    }

    public V setValue(V value) {
        if (value == null)
            throw new NullPointerException();

        V oldValue = this.value;
        this.value = value;
        return oldValue;
    }

    public boolean equals(Object o) {
        if (!(o instanceof Map.Entry))
            return false;
        Map.Entry<?,?> e = (Map.Entry<?,?>)o;

        return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
           (value==null ? e.getValue()==null : value.equals(e.getValue()));
    }

    public int hashCode() {
        return hash ^ Objects.hashCode(value);
    }

    public String toString() {
        return key.toString()+"="+value.toString();
    }
}

3.常用方法-put&get

##### 1.put
public synchronized V put(K key, V value) {
    //判断value值是否为空,如果为空则抛出异常,与HashMap不同,
    //hashMap允许key,value为空
    if (value == null) {
        throw new NullPointerException();
    }
    // Makes sure the key is not already in the hashtable.
  // 确保hashtable中没有重复的key
  // 保存当前的table数据
    Entry<?,?> tab[] = table;
  // 当前key值的哈希值
    int hash = key.hashCode();
  //确保当前hashtable中没有key值的存在:
 	 /*将当前的哈希值与最大数组长度进行与运算后对数组长度进行求余
 	   获取到key对应数组中的索引值 */
    int index = (hash & 0x7FFFFFFF) % tab.length;
    @SuppressWarnings("unchecked")
  //找到entry数组中对应下标的entry对象
    Entry<K,V> entry = (Entry<K,V>)tab[index];
  //从获取到的entry对象开始循环,查看是否存在有对应的key,hash存在
  /* 如果对应下标中已经存在了entry对象,且hash和key值相等,
  那么将新值替换旧值,并返回旧值*/
    for(; entry != null ; entry = entry.next) {
        if ((entry.hash == hash) && entry.key.equals(key)) {
            V old = entry.value;
            entry.value = value;
            return old;
        }
    }
   //如果对应下标处没有对象,那么添加一个新的entry对象
    addEntry(hash, key, value, index);
    return null;
}
private void addEntry(int hash, K key, V value, int index) {
  	//添加一次,修改的次数增加
    modCount++;
  	//记录下当前的entry数组
    Entry<?,?> tab[] = table;
    //如果entry数组中的数量已经大于等于了阈值那么进行扩容
    if (count >= threshold) {
        rehash();
      
        tab = table;
        hash = key.hashCode();
        index = (hash & 0x7FFFFFFF) % tab.length;
    }
   	//创建一个新的entry,添加到对应索引处
    Entry<K,V> e = (Entry<K,V>) tab[index];
    tab[index] = new Entry<>(hash, key, value, e);
    //记录的保存的entry对象的数量增加
    count++;
}
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数组用于保存数据
    Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
  //结构修改次数增加
    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;
        }
    }
}
2.get
public synchronized V get(Object key) {
    Entry<?,?> tab[] = table;
    //计算出对应的索引值
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
   //判断对应索引值上的key和hash值是否相等,如果相对,返回对应索引值的对象。 
    for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
        if ((e.hash == hash) && e.key.equals(key)) {
            return (V)e.value;
        }
    }
    return null;
}

4.HashTable与HashMap不同

1 继承和实现的类不同:hashtable继承了Dictionary类,hashmap继承了AbstractMap类。

public class Hashtable<K,V>
	extends Dictionary<K,V>
	implements Map<K,V>, Cloneable, java.io.Serializable 
	
public class HashMap<K,V>
	extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable

2 结构不同:HashTable是实现了拉链法的散列表(拉链法只知道是解决哈希冲突具体还没有进行了解)

​ HashMap是具有数组,链表,红黑树多种结构

3 安全性不同: HashTable 中的puta方法,

​ public synchronized V put(K key, V value)从源码上可以看出

​ hashtable是同步的。而hashmap是非同步的。

4内容上不同: HashTable不允许key,value值为空,当value值为空,还会抛出异常,还采取了计算对应的链表中索引值的方式确保key不为空。 而HashMap允许key,value值可以为空。

5 初始容量不同: Hashtable默认是11,Hashmap 默认是16

6 扩充的容量不同:Hashtable是newCapacity = (oldCapacity << 1) + 1,

​ HashMap是 newCap = oldCap << 1

关于HashTable中不允许key,value值为空

​ HashTable是继承了Dictionary类,这是一个抽象类,在这个抽象类中的put方法中声明了key,value值都不能为null.

public abstract
class Dictionary<K,V>
/*  Maps the specified key to the specified
    value in this dictionary. Neither the key nor the
    value can be null.(key,value值都不能为null)*/
abstract public V put(K key, V value);

你可能感兴趣的:(JavaSE)