集合--Hashtable

12 篇文章 0 订阅

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()

Hashtable 多线程下安全;HashMap 线程不安全

synchronized:调用synchronized修饰的方法,对对象进行加锁(互斥:同一时刻同一资源只有一个线程访问)
  • 思路:
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特点总结:
  1. null值:键值对不能为null

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

  3. key重复性:key不能重复

  4. 无序性:不能保证插入有序

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值