JDK源码解析---HashTable

1.概述

和HashMap一样,Hashtable 也是一个散列表,它存储的内容是键值对(key-value)映射。Hashtable 的函数都是同步的,这意味着它是线程安全的。它的key、value都不可以为null。

与HashMap不同点

  1. 在于table数组的初始化时机不同,HashTable对于table数组的初始化时机在构造函数中。而HashMap是在resize()中,即在第一次发生put键值对的时候才对table数组进行初始化。
  2. table数组的大小不同,HashTable可以是任意数值。HashMap只能是2的n次方的数值。
  3. 阈值初始化的计算方式不同,HashMap根据initialCapacity的值来得到一个最小的比他大的一个2的n次方的数当作阈值。而HashTable根据Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1)
  4. 默认容量为11。每次扩容是旧容量的2倍+1。而HashMap默认容量为16。每次扩容是旧容量的2倍

2.类图

在这里插入图片描述

继承了Dictionary,Dictionary是个被废弃的抽象类。

实现了Map接口,序列化和可扩容接口

3.属性

private transient Entry<?,?>[] table;//用于存储键值对的Entry数组 默认大小11
private transient int count;//统计键值对的数量

private int threshold;//扩容阈值

private float loadFactor;//装载因子

private transient int modCount = 0;//修改次数

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;//数组大小最大值

4.构造方法

  1. 无参构造函数 内部调用有参的构造函数 默认大小11 装载因子0.75

    public Hashtable() {
        this(11, 0.75f);
    }
    
  2. Hashtable(int initialCapacity)

    初始化HashTable的大小的构造函数,同样默认的装载因子0.75

    public Hashtable(int initialCapacity) {
        this(initialCapacity, 0.75f);
    }
    
  3. Hashtable(Map<? extends K, ? extends V> t)

    public Hashtable(Map<? extends K, ? extends V> t) {
        this(Math.max(2*t.size(), 11), 0.75f);//根据t的大小来决定初始化HashTable中Entry数组的大小
        putAll(t);
    }
    
  4. Hashtable(int initialCapacity, float loadFactor)

    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)//若参数为0,则创建一个大小为1的HashTable
            initialCapacity = 1;
        this.loadFactor = loadFactor;//这只装载因子
        table = new Entry<?,?>[initialCapacity];//初始化table数组
        threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);//初始化阈值
    }
    

5.简单的基本方法

  • size() 获得键值对的数量
public synchronized int size() {
    return count;
}
  • isEmpty() 判断是否为空
public synchronized boolean isEmpty() {
    return count == 0;
}
  • keys() 获得键枚举器对象.这个对象实现了迭代器接口。可以使用迭代的方式得到所有的键。
public synchronized Enumeration<K> keys() {
    return this.<K>getEnumeration(KEYS);
}
private static final int KEYS = 0;
private static final int VALUES = 1;
private static final int ENTRIES = 2;
private <T> Enumeration<T> getEnumeration(int type) {
    if (count == 0) {
        return Collections.emptyEnumeration();
    } else {
        return new Enumerator<>(type, false);
    }
}
  • elements() 获得值枚举器对象。和上面类似可以通过迭代的方式得到所有的值
public synchronized Enumeration<V> elements() {
  	return this.<V>getEnumeration(VALUES);
}
  • keySet() 得到所有key的集合
public Set<K> keySet() {
    if (keySet == null)
        keySet = Collections.synchronizedSet(new KeySet(), this);
    return keySet;
}

6.查找

6.1 contains(Object value)

判断值存不存在

public synchronized boolean contains(Object value) {
    if (value == null) {
        throw new NullPointerException();
    }

    Entry<?,?> tab[] = table;
    for (int i = tab.length ; i-- > 0 ;) {//遍历数组
        for (Entry<?,?> e = tab[i] ; e != null ; e = e.next) {//遍历结点
            if (e.value.equals(value)) {//找到返回true
                return true;
            }
        }
    }
    return false;//否则返回false
}

6.2 containsValue(Object value)

唯一的区别就是没有加synchronized

public boolean containsValue(Object value) {
    return contains(value);
}

6.3 containsKey(Object key)

判断key存不存在

public synchronized boolean containsKey(Object key) {
    Entry<?,?> tab[] = table;
    int hash = key.hashCode();//计算hash
    int index = (hash & 0x7FFFFFFF) % tab.length;//找到数组中存储的坐标
    for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
        if ((e.hash == hash) && e.key.equals(key)) {//遍历这一位置的链表。若key相同返回true
            return true;
        }
    }
    return false;//否则返回false
}

6.4 get(Object key)

根据key得到对应的value 和上面查找有没有key存在类似 只是返回的结果不同

找到了对应的key返回value 没有找到返回null

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;
}

7.添加单个结点

7.1 addEntry(int hash,K key,V value,int index)

private void addEntry(int hash, K key, V value, int index) {
    modCount++;

    Entry<?,?> tab[] = table;
    if (count >= threshold) {// 若容量超过阈值 扩容
        // Rehash the table if the threshold is exceeded
        rehash();

        tab = table;
        hash = key.hashCode();//计算hash
        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);//链表法的方式加入新结点
    count++;
}

7.2 put(K key,V value)

添加键值对到map中,若已存在key 覆盖它并返回旧值,否则添加一个新结点到map中,返回null。

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();//计算hash
    int index = (hash & 0x7FFFFFFF) % tab.length;//计算索引
    @SuppressWarnings("unchecked")
    Entry<K,V> entry = (Entry<K,V>)tab[index];//拿到那一位置的链表
    for(; entry != null ; entry = entry.next) {//遍历链表
        if ((entry.hash == hash) && entry.key.equals(key)) {//key存在,修改对应的value
            V old = entry.value;
            entry.value = value;
            return old;//返回旧值
        }
    }

    addEntry(hash, key, value, index);//否则调用addEntry添加新结点。
    return null;
}

8.扩容rehash()

扩容后新的容量是旧容量的2倍加1。在不超过最大容量的范围内。否则就是扩到最大容量。使用的是头插法。

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

    // overflow-conscious code
    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];//根据新容量创建新的数组

    modCount++;
    threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);//计算阈值
    table = newMap;//新数组赋值给table

    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;//计算再次hash计算新存储的位置。
            e.next = (Entry<K,V>)newMap[index]; //使用的是头插法
            newMap[index] = e;
        }
    }
}

9.移除

9.1remove(Object key)

根据给定的key 移除键值对。

public synchronized V remove(Object key) {
    Entry<?,?> tab[] = table;
    int hash = key.hashCode();//计算hash
    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)) {//找到对应的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;//没有找到对应的key返回null
}

9.2clear() 清空

public synchronized void clear() {
    Entry<?,?> tab[] = table;
    modCount++;
    for (int index = tab.length; --index >= 0; )//数组全部置为null
        tab[index] = null;
    count = 0;
}

10.其他方法

public synchronized boolean equals(Object o)//比较是否相同
  
public synchronized int hashCode()//计算hash
  
public synchronized V getOrDefault(Object key, V defaultValue)//和HashMap一样,有对应的value返回value 没有就返回defaultValue
  
public synchronized V putIfAbsent(K key, V value)//不存在则put 若存在 判断对应的value是不是为null 为null的话改变这个值。这是和HashMap不一样的地方
  
public synchronized boolean replace(K key, V oldValue, V newValue)//替换
  
public synchronized V replace(K key, V value)//替换 和上面不同的地方在于,上面必须key对应的value和参数oldValue相同是 才可以替换。这里是找到key对应的value就替换

猜你喜欢

转载自blog.csdn.net/gongsenlin341/article/details/107999751