HashMap(三)Hashtable(JDK1.7)

本文详细解析了Hashtable的数据结构、工作原理及特点。介绍了其底层由散列表构成,Entry<K,V>维护,以及扩容机制、增删改查方法。强调线程安全性和不可存储null值的特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(一)底层数据结构

HashTable的数据结构和HashMap的数据结构相同,都是有散列表构成的,内部都是由Entry<K,V>来维护的。

(二)继承关系

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

继承Dictionary,实现了Map、Cloneable接口

(三)默认值

默认大小:11

默认加载因子:0.75

(四)基本属性

    /**
     * 哈希表
     */
    private transient Entry<K,V>[] table;

    /**
     * 元素个数
     */
    private transient int count;

    /**(int)(capacity * loadFactor)
     *扩容阈值
     */
    private int threshold;

    /**
     *加载因子
     */
    private float loadFactor;

(五)构造函数

(1)


    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);
        initHashSeedAsNeeded(initialCapacity);
    }

(2)


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

(3)


    public Hashtable() {
        this(11, 0.75f);
    }

(4)


    public Hashtable(Map<? extends K, ? extends V> t) {
        this(Math.max(2*t.size(), 11), 0.75f);
        putAll(t);
    }

(六)增长方式

(七)CURD(增删改查)

1、put()方法——添加键值对

(一)判断value不能为null,如果为null,抛出空指针异常;

(二)通过key的哈希,得到索引位置

(三)对该索引位置的链表进行遍历,key是否存在,(条件:if ((e.hash == hash) && e.key.equals(key)))

(四)在key存在的情况下,将key的value值进行更新,并返回原来的value值

(五)key不存在,创建新结点,进行插入;

            (1)扩容考虑:count >= threshold

            (2)新容量大小,(2倍加一)

            (3)将原哈希表中所有的数据进行重新哈希,至新的哈希表中

            (4)更新插入的key的新位置

            (5)找到新结点位置,创建新结点,通过头插法将元素插入

public synchronized V put(K key, V value) {
        // Make sure the value is not null
        //value不能为null,抛出空指针异常
        if (value == null) {
            throw new NullPointerException();
        }

        // Makes sure the key is not already in the hashtable.
        Entry tab[] = table;
        //key也不能为null
        int hash = hash(key);//通过key的哈希
        int index = (hash & 0x7FFFFFFF) % tab.length;//hash过程
        for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                V old = e.value;
                e.value = value;
                return old;
            }
        }

        modCount++;
		//元素个数大于容量的0.75,扩容
        if (count >= threshold) {
            // Rehash the table if the threshold is exceeded
            rehash();

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

        // Creates the new entry.
        Entry<K,V> e = tab[index];
        tab[index] = new Entry<>(hash, key, value, e);
        count++;
        return null;
    }

rehash()方法——扩容方法

    protected void rehash() {
        int oldCapacity = table.length;
        Entry<K,V>[] 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<K,V>[] newMap = new Entry[newCapacity];//扩容

        modCount++;
		//更新扩容阈值,取新容量*加载因子与最大容量加一中的最小值
        threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
        boolean rehash = initHashSeedAsNeeded(newCapacity);

        table = newMap;

		//重新计算hash值
        for (int i = oldCapacity ; i-- > 0 ;) {
            for (Entry<K,V> old = oldMap[i] ; old != null ; ) {
                Entry<K,V> e = old;
                old = old.next;

                if (rehash) {
                    e.hash = hash(e.key);
                }
                int index = (e.hash & 0x7FFFFFFF) % newCapacity;
                e.next = newMap[index];
                newMap[index] = e;
            }
        }
    }

2、get()——获取元素

具有线程安全性

(1)通过key来哈希,得到key存储的索引位置

注:key为null,也会抛出空指针异常

(2)遍历当前索引位置结点,判断是否相等,找到则直接返回value,未找到返回null

    public synchronized V get(Object key) {
        Entry tab[] = table;
        int hash = hash(key);//根据key哈希,得到相应的索引位置
        int index = (hash & 0x7FFFFFFF) % tab.length;
		//遍历当前索引位置结点
        for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {//哈希值相等,键值相等,返回value
                return e.value;
            }
        }
        return null;//没有找到返回null
    }

3、remove()——删除操作(线程安全)

(1)通过key获取当前索引位置

(2)对该索引位置链表进行遍历,找到key所对应的entry实体,并进行删除,返回对应的value,未删除返回null

   public synchronized V remove(Object key) {
        Entry tab[] = table;
        int hash = hash(key);//根据key哈希
        int index = (hash & 0x7FFFFFFF) % tab.length;
        for (Entry<K,V> e = tab[index], 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;//直接将next作为当前索引位置链表的头结点
                }
                count--;//count减一
                V oldValue = e.value;
                e.value = null;//便于回收
                return oldValue;//返回删除键的value
            }
        }
        return null;
    }

(八)特点

1.维护Hashtable内部的是一个Entry,默认容量为11,负载因子为0.75,

2.扩容方式当Entry已用空间>=总空间的75%,总空间扩大到原来的2倍。(因为每次扩容都要计算桶位,这样扩容可以减少扩容次数)

3.Hashtable所有的方法都有synchronized修饰线程安全

4. Hashtable不可以存Null值,

5.HashTable的容量指的是Entry<K,V>[] table的长度

6.计算桶位的方式int index= (hash & 0x7FFFFFFF) % tab.length
 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值