Java集合系列08之 Hashtable

Java集合系列08之 Hashtable

参考:jdk 1.7 源码

第1部分 Hashtable介绍

  1. 和HashMap一样,Hashtable 也是一个散列表,它存储的内容是键值对(key-value)映射。
  2. Hashtable 继承于Dictionary,实现了Map、Cloneable、java.io.Serializable接口。
  3. Hashtable 的函数都是同步的,这意味着它是线程安全的;
  4. Hashtable 的key、value都不可以为null
  5. Hashtable 中的映射是无序的。
  6. 扩容:新容量 = 旧容量*2+1 ;且不能超过极限值。
  7. HashTable在实例化时即分配内存,与HashMap在进行第一个put时分配内存有区别。
  8. put操作:根据key值通过下图操作获取下标,存入主链数组下标所在位置,如果发生hash冲突,则有一个应为指向原先存储的Entry。
    在这里插入图片描述
  9. get操作:根据key值通过下图操作获取下标,如果不存在冲突链,直接返回对应Entry,如果存在冲突链,遍历冲突链获取对应Entry
    在这里插入图片描述

第2部分 Hashtable的整体结构

在这里插入图片描述
和HashMap一样,Hashtable由数组+链表组成的,数组是Hashtable的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表(当前entry的next指向null),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度为O(n),首先遍历链表,存在即覆盖,否则新增;对于查找操作来讲,仍需遍历链表,然后通过key对象的equals方法逐一比对查找。所以,性能考虑,HashMap中的链表出现越少,性能才会越好。

第3部分 Hashtable的核心属性

和HashMap 基本一样

// Hashtable主干
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;

//实际存储的key-value键值对的个数
transient int count;

//阈值,当table == {}时,该值为初始容量(初始容量默认为11);
//为table分配内存空间后,threshold一般为 capacity*loadFactory。Hashtable在进行扩容时需要参考threshold
int threshold;

//负载因子,代表了table的填充度有多少,默认是0.75
final float loadFactor;

//Hashtable结构修改的次数。
//用于快速失败,由于HashMap非线程安全,在对HashMap进行迭代时,
//如果期间其他线程的参与导致HashMap的结构发生变化了(比如put,remove等操作),
//需要抛出异常ConcurrentModificationException
transient int modCount

// Entry 内部类
private static class Entry<K,V> implements Map.Entry<K,V> {
        int hash;                //hash值
        final K key;             //key
        V value;				 //value
        Entry<K,V> next;	     //冲突链引用(单向链表),没冲突就指向null
 }

// volatile 关键字保证同步
private transient volatile Set<K> keySet = null;
private transient volatile Set<Map.Entry<K,V>> entrySet = null;
private transient volatile Collection<V> values = null;

第4部分 Hashtable源码解析

put 操作

public synchronized V put(K key, V value) {
     // 保证 value 不为空
     if (value == null) {
         throw new NullPointerException();
     }

     // 判断 key 值是否已存在,存在则修改value,否则新增
     Entry tab[] = table;
     int hash = hash(key);    // 获取hash值
     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)) {
             V old = e.value;
             e.value = value;
             return old;
         }
     }

     modCount++;
     // 判断容量,如果扩容,根据新HashTable重新计算hash值 和 下标
     if (count >= threshold) {
         // 扩容
         rehash();

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

     // 新增节点
     Entry<K,V> e = tab[index];
     tab[index] = new Entry<>(hash, key, value, e);
     count++;
     return null;
 }

 // hash 计算参数 , 保证散列均匀
 transient int hashSeed;

 // 初始化 hashSeed
 final boolean initHashSeedAsNeeded(int capacity) {
     boolean currentAltHashing = hashSeed != 0;
     boolean useAltHashing = sun.misc.VM.isBooted() &&
             (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
     boolean switching = currentAltHashing ^ useAltHashing;
     if (switching) {
         hashSeed = useAltHashing
             ? sun.misc.Hashing.randomHashSeed(this): 0;
     }
     return switching;
 }

// hash 函数
 private int hash(Object k) {
     // 如果禁用散列,哈希种子将为零。
     return hashSeed ^ k.hashCode();
 }

    // 扩容
    protected void rehash() {
        int oldCapacity = table.length;
        Entry<K,V>[] oldMap = table;

        // 扩容:新容量 = 旧容量*2+1
        int newCapacity = (oldCapacity << 1) + 1;
        // 保证扩容不能超过极限值
        if (newCapacity - MAX_ARRAY_SIZE > 0) {
            if (oldCapacity == MAX_ARRAY_SIZE)
                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计算,存入新Hashtable中
        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;
            }
        }
    }

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

第5部分 Hashtable遍历方式

  1. 遍历Hashtable的键值对
Iterator iter = table.entrySet().iterator();
while(iter.hasNext()) {
    Map.Entry entry = (Map.Entry)iter.next();
    Object key = entry.getKey();
    Object value= entry.getValue();
}
  1. 通过Iterator遍历Hashtable的键
Iterator iter = table.keySet().iterator();
while (iter.hasNext()) {
    Object key = iter.next();
    Object  value= table.get(key);
}
  1. 通过Iterator遍历Hashtable的值
Collection c = table.values();
Iterator iter= c.iterator();
while (iter.hasNext()) {
    Object value = iter.next();
}
  1. 通过Enumeration遍历Hashtable的键
Enumeration enu = table.keys();
while(enu.hasMoreElements()) {
    System.out.println(enu.nextElement());
}   
  1. 通过Enumeration遍历Hashtable的值
Enumeration enu = table.elements();
while(enu.hasMoreElements()) {
    System.out.println(enu.nextElement());
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值