HashTable1.8源码解析

HashTable1.8介绍:

HashTable的底层实现是由“数组+链表”来实现的。和HashMap一样,Hashtable 也是一个散列表,它存储的内容是键值对(key-value)映射。无论key还是value都不能为null,且线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低。但是Hashtable是一种能提供快速插入和查询的数据结构,无论其包含多少Item(条目),执行查询和插入操作的平均时间复杂度总是接近O(1)

HashTable源码解析:

HashTable的继承关系:
Hashtable 继承于Dictionary,实现了Map、Cloneable、java.io.Serializable接口。

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

HashTable中的成员参数:

/**
     * The hash table data.
     *
     * 数据保存数组  和hashMap中的tab同理
     */
    private transient Entry<?,?>[] table;

    /**
     * The total number of entries in the hash table.
     *
     * 总条数  和hashMap中的size同理
     */
    private transient int count;

    //当表的大小超过此阈值时,将重新对其进行散列。(该字段的*值为(Int)(容量*加载因子))。
    //table.length * loadFactor = threshold
    private int threshold;

    //加载因子 
    private float loadFactor;
    //修改次数
    private transient int modCount = 0;

HashTable的构造方法:

	//可指定初始容量和加载因子
	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;//初始容量最小值为1  
        this.loadFactor = loadFactor;  
        table = new Entry[initialCapacity];//创建桶数组  
        threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);//初始化容量阈值  
        useAltHashing = sun.misc.VM.isBooted() &&  
                (initialCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);  
    }  
    /** 
     * Constructs a new, empty hashtable with the specified initial capacity 
     * and default load factor (0.75). 
     */  
    //默认负载因子为0.75
    public Hashtable(int initialCapacity) {  
        this(initialCapacity, 0.75f);  
    }  
    //默认容量为11,负载因子为0.75
    public Hashtable() {  
        this(11, 0.75f);  
    }  
    /** 
     * Constructs a new hashtable with the same mappings as the given 
     * Map.  The hashtable is created with an initial capacity sufficient to 
     * hold the mappings in the given Map and a default load factor (0.75). 
     */  
    public Hashtable(Map<? extends K, ? extends V> t) {  
        this(Math.max(2*t.size(), 11), 0.75f);  
        putAll(t);  
    }  

HashTable的原理图:
在这里插入图片描述
hash数组—>Entry[];
每个数组元素Entry<>元素中保存的是一个单向链表的头结点。
注意:
1.Hashtable的默认容量为11,默认负载因子为0.75
2.Hashtable的容量可以为任意整数,最小值为1
3.跟HashMap一样,Hashtable内部也有一个静态类叫Entry,其实是个键值对对象,保存了键和值的引用。
4.HashTable和HashMap1.7一样也是通过链表来解决hash冲突的。

HashTable的API:

synchronized void                clear()
synchronized Object              clone()
             boolean             contains(Object value)
synchronized boolean             containsKey(Object key)
synchronized boolean             containsValue(Object value)
synchronized Enumeration<V>      elements()
synchronized Set<Entry<K, V>>    entrySet()
synchronized boolean             equals(Object object)
synchronized V                   get(Object key)
synchronized int                 hashCode()
synchronized boolean             isEmpty()
synchronized Set<K>              keySet()
synchronized Enumeration<K>      keys()
synchronized V                   put(K key, V value)
synchronized void                putAll(Map<? extends K, ? extends V> map)
synchronized V                   remove(Object key)
synchronized int                 size()
synchronized String              toString()
synchronized Collection<V>       values()
HashTable的重要对外接口

Clear()方法:
clear() 的作用是清空Hashtable。它是将Hashtable的table数组的值全部设为null

public synchronized void clear() {
    Entry tab[] = table;
    modCount++;
    for (int index = tab.length; --index >= 0; )
        tab[index] = null;
    count = 0;
}

put()方法:
HashMap中key和value可以是null,HashTable中是不可以的。

/**
     * Maps the specified <code>key</code> to the specified
     * <code>value</code> in this hashtable. Neither the key nor the
     * value can be <code>null</code>. <p>
     *
     * The value can be retrieved by calling the <code>get</code> method
     * with a key that is equal to the original key.
     *
     * @param      key     the hashtable key
     * @param      value   the value
     * @return     the previous value of the specified key in this hashtable,
     *             or <code>null</code> if it did not have one
     * @exception  NullPointerException  if the key or value is
     *               <code>null</code>
     * @see     Object#equals(Object)
     * @see     #get(Object)
     */
    // todo 这类加锁保证线程安全 这里就是实例锁,性能相对于concurrentHashMap的分段锁来说  比较慢
    public synchronized V put(K key, V value) {
        // Make sure the value is not null

        // todo 这里说明hashTable中的value不能为空
        if (value == null) {
            throw new NullPointerException();
        }

        // 查找key对应的数组下标  以便获取所在的链表
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;

        //这里判断是否存在当前key

        @SuppressWarnings("unchecked")
        Entry<K,V> entry = (Entry<K,V>)tab[index];
        for(; entry != null ; entry = entry.next) {
            // 这里也不太一样  hashMap中可以设置判断value是否相等类判断是否覆盖老value
            // hashMap中相当于 就有一个cas的原理可供选择
            if ((entry.hash == hash) && entry.key.equals(key)) {
                V old = entry.value;
                entry.value = value;
                return old;
            }
        }

        addEntry(hash, key, value, index);
        return null;
    }

addEntry() 方法:
这里面就是实际的插入过程,但是这里面判断了是否需要扩容,没有判断是否已经包含,是否包含都是在调用这个方法前判断的。

	//这里没有判断  原来的table中是否已经包含这个key  在外面判断的  这里直接加上去
    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();
            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++;
    }

rehash()方法:
他也是和hashmap差不多 就是直接扩容两倍,但是hashTable中会在加1,并且扩容后要重新计算每个元素对应的数组位子,相对于hashMap来说性能会有点差距,hashMap少一步取余的计算,他们在插入数据时都是头插法。

	//扩容操作
    @SuppressWarnings("unchecked")
    protected void rehash() {
        int oldCapacity = table.length;
        Entry<?,?>[] oldMap = table;

        //hashMap中的length 都市2的次方倍数并且扩容都是 * 2的  但是hashTable 实在原来的基础上* 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];

        modCount++;
        // 这里计算  下次扩容的阈值
        threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
        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;
                // 这里是重新计算hash 但是hashMap中优化的比较好,不需要重新计算, 根据二进制来 判断hash和老长度 与运算 是否大于0 大于则在 老长度加上再老数组中的下标  就是新下标  小于则下标不变
                int index = (e.hash & 0x7FFFFFFF) % newCapacity;
                // 这里相同hashMap和 hashTable都是放在第一个
                e.next = (Entry<K,V>)newMap[index];
                newMap[index] = e;
            }
        }
    }

其他方法都比较简单。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值