集合--HashTable

Hashtable
源码探究

存在包:
 package java.util。
继承关系

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

继承了Dictionary(JDK较早而提供的实现类),实现了Map接口,可以克隆,可以序列化;

底层数据结构:
 哈希表(数组+链表)。
基本属性:

private transient Entry<K,V>[] table;//哈希表  数组大小默认值11
private transient int count;//哈希表中的实体个数
private int threshold;//扩容的阈值
private float loadFactor;//加载因子
private transient int modCount = 0;
private static final long serialVersionUID = 1421746759512286392L;//序列号
static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;

构造函数:

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];  //数组初始化  与hashmap不同
        threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
    	//扩容阈值初始化
        initHashSeedAsNeeded(initialCapacity);
    }

    /**
     * Constructs a new, empty hashtable with the specified initial capacity
     * and default load factor (0.75).
     */
    public Hashtable(int initialCapacity) {
        this(initialCapacity, 0.75f);
    }

    /**
     * Constructs a new, empty hashtable with a default initial capacity (11)
     * and load factor (0.75).  数组默认大小11,加载因子默认0.75
     */
    public Hashtable() {
        this(11, 0.75f);
    }

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


增长方式:
 以存储的数组二倍长度加一的方式增长,2 * table.length + 1。

CRUD方法研究(增删改查):
1.添加元素:put()

思路:
1.判断value是否为null;若为null抛出异常 ====》hashtable中value不能为null

2.通过key进行hash获取到key该存储的索引位置

3.该索引位置的链表进行遍历,获取key是否存在(key存在条件:hash是否相等,key使用equals())

4.在存在该key的情况下,将value值进行更新并且直接返回

5.key不存在则进行新节点插入

	5.1 扩容考虑:(count >= threshold )entry结点个数大于扩容阈值;

	5.2 新容量大小为:2 * table.length

	5.3 将原哈希表中的数据全部进行重新hash到新的哈希表中

	5.4 更新插入的key的新的位置

	5.5 找到新节点的位置,创建entry实体通过头结点将元素进行插入

public synchronized V put(K key, V value) {
        // Make sure the value is not null
        if (value == null) {
            throw new NullPointerException();//特点:value 不能为null
        }

        // Makes sure the key is not already in the hashtable.
        //key不能为null,key为null也会抛出空指针异常
        Entry tab[] = table;
    //通过key的哈希找到索引位置
        int hash = hash(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)) {//未使用双等key判断
                V old = e.value;
                e.value = value;
                return old;
            }
        }

        modCount++;
        if (count >= threshold) {//count 实体个数 扩容条件
            // Rehash the table if the threshold is exceeded 重hash
            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;
    }


//hash方式与数组长度相关  
protected void rehash() {
        int oldCapacity = table.length;
        Entry<K,V>[] oldMap = table;

        // overflow-conscious code
        int newCapacity = (oldCapacity << 1) + 1;//增长方式2倍加一
        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;

        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;
            }
        }
    }
private int hash(Object k) {
        // hashSeed will be zero if alternative hashing is disabled.
        return hashSeed ^ k.hashCode();
    }
 public synchronized int hashCode() {
        int h = 0;
        if (count == 0 || loadFactor < 0)
            return h;  // Returns zero

        loadFactor = -loadFactor;  // Mark hashCode computation in progress
        Entry[] tab = table;
        for (Entry<K,V> entry : tab)
            while (entry != null) {
                h += entry.hashCode();
                entry = entry.next;
            }
        loadFactor = -loadFactor;  // Mark hashCode computation complete

        return h;
    }

2.删除元素:remove():通过key进行查找

  • 删除操作本身是线程安全的

  • 思路:

    1.通过key获取到当前存储的索引位置

    2.对该索引位置下的链表进行遍历,找到key所对应的entry实体,并进行删除;

public synchronized V remove(Object key) {
        Entry tab[] = table;
    //通过key哈希到指定索引位置
        int hash = hash(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;
                }
                count--;
                V oldValue = e.value;
                e.value = null;
                return oldValue;
            }
        }
        return null;
    }

3.查找元素:get():通过key进行查找

思路:

1.通过key的哈希获取到存储的索引位置 —》通过key为null进行get操作也会抛出异常

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

特点: get()本身具有线程安全性!

public synchronized V get(Object key) {
        Entry tab[] = table;
        int hash = hash(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)) {
                return e.value;
            }
        }
        return null;
    }
Hashtable方法总结:

1.int size():存储键值对个数。
2.boolean isEmpty():  测试此哈希表是否没有键映射到值。
3.boolean contains(Object value): 测试此映射表中是否存在与指定值关联的键。
4.boolean containsKey(Object key):测试指定对象是否为此哈希表中的键。
5.Object get(Object key): 返回指定键所映射到的值,如果此映射不包含此键的映射,则返回 null. 更确切地讲,如果此映射包含满足 (key.equals(k)) 的从键 k 到值 v 的映射,则此方法返回 v;否则,返回 null。
6.void rehash(): 增加此哈希表的容量并在内部对其进行重组,以便更有效地容纳和访问其元素。
7.Object put(Object key, Object value): 将指定 key 映射到此哈希表中的指定 value。
8.Object remove(Object key):从哈希表中移除该键及其相应的值。
9.void clear(): 将此哈希表清空,使其不包含任何键。
10.String toString():返回此 Hashtable 对象的字符串表示形式,其形式为 ASCII 字符 ", " (逗号加空格)分隔开的、括在括号中的一组括号

Hashtable特点总结:
null值:键值对不能为null;

底层数据结构:数组+链表

key重复性:key不能重复;

无序性:不能保证插入有序
HashMap和Hashtable的异同点
相同点:
1.底层数据结构:都为数组 + 链表;

2.key都不能重复;

3.插入元素都不能保证插入有序;

4.哈希过程通过key进行哈希;

不同点:
1.扩容方式不同: HashMap以2 * table.length的方式;Hashtable以2 * table.length + 1的方式;

2.继承关系:HashMap继承AbstractMap;Hashtable继承Dictionary;

3.安全性问题:HashMap不能保证线程安全;Hashtable能保证线程安全;

4.null值问题:HashMap的key、value都可以为null,put方法下对key为null不为null分别进行处理;Hashtable的key、value不能为null,put方法下key、value都不能为null,为null则会抛出空指针异常;

5.默认值:HashMap默认数组大小为16;Hashtable默认数组大小为11;

6.hash算法不同;

7.效率不同:HashMap在单线程下效率高;Hashtable在单线程下效率低;()Sychronized:从用户态转换到内核态,再转到内核态。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值