集合概述08

Hashtable

  • 线程安全的,不允许null的键或值;是线程安全的但是Hashtable线程安全的策略实现代价却太大了,简单粗暴,get/put所有相关操作都synchronized的,这相当于给整个哈希表加了一把大锁。多线程访问时候,只要有一个线程访问或操作该对象,那其他线程只能阻塞,相当于将所有的操作串行化,在竞争激烈的并发场景中性能就会非常差

  • HashMap允许键和值为null,只是key只能有一个null,值允许null多个

  • 由于线程安全,则在非多线程环境下不建议使用

  • 类定义

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

  • Hashtable是一个散列表,它存储的内容是键值对(key-value)映射。通过"拉链法"实现的哈希表

  • Hashtable继承于Dictionary,实现了Map、Cloneable、java.io.Serializable接口。

  • 属性

  • private transient Entry<?,?>[] table; 是用于存储单向链表的数组

  • 内部类Entry

  • private static class Entry<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    Entry<K,V> next;

  • private transient int count;集合中存储的元素个数

  • private int threshold;对应存储扩容的阈值 (int)(capacity * loadFactor)

  • private float loadFactor;负载因子值,一般建议(0,1)

  • private transient int modCount = 0;修改次数

  • 构造器

  • 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); 获取扩容的阈值
    

    }
    MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8

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

    public Hashtable() { 无参构造器的默认值 容积为11,负载因子0.75
    this(11, 0.75f);
    }
    public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR; 在第一次添加put数据时,才进行数组的初始化操作,初始值为16,负载因子0.75
    }

    成员方法:put方法

    public synchronized V put(K key, V value) { 线程安全,以当前hashtable对象充当锁
    if (value == null) { value值不允许为空
    throw new NullPointerException();
    }
    Entry<?,?> tab[] = table;
    int hash = key.hashCode(); 直接获取key的hash值,但是HashMap中专门定义了hash方法,用于对key的hashCode值进行扰动处理
    int index = (hash & 0x7FFFFFFF) % tab.length; 去除hash值中的符号位求余计算获取数组的对应下标索引
    Entry<K,V> entry = (Entry<K,V>)tab[index]; 获取对应位置上的链头
    遍历整个链表,查找key值相同的Entry对象【首先进行hash值比较,如果相等则调用equals方法】,如果相等则后盖前
    for(; entry != null ; entry = entry.next) {
    if ((entry.hash == hash) && entry.key.equals(key)) {
    V old = entry.value;
    entry.value = value;
    return old;
    }
    }
    如果链表上没有相等的key,则添加entry对象到链表上
    addEntry(hash, key, value, index); 参数1是key的hash值,并没有进行扰动处理,参数2key,参数3value,参数4是数组索引序号
    return null;
    }

    private void addEntry(int hash, K key, V value, int index) {
    Entry<?,?> tab[] = table;
    if (count >= threshold) {如果当前集合中的元素个数大于阈值,则进行扩容处理,每次长度为 2倍+1
    rehash(); 然后针对目前的所有元素重新进行遍历,计算新位置
    tab = table;
    hash = key.hashCode();
    index = (hash & 0x7FFFFFFF) % tab.length;
    }
    Entry<K,V> e = (Entry<K,V>) tab[index];
    tab[index] = new Entry<>(hash, key, value, e); 将新建Entry对象,并存储到对应的位置上
    count++;
    modCount++;
    }

  • Hashtable的函数都是同步的,这意味着它是线程安全的。它的key、value都不可以为null。此外,Hashtable中的映射不是有序的;在hashmap中允许key和value 为null,只是key只能有一个null,如果出现冲突后盖前;如果使用null的key和value则会出现一个运行时异常NullPonterException

HashMap 与 HashTable区别
    1. 线程安全:HashMap是非线程安全的,HashTable是线程安全的;HashTable内部的方法基本都经过synchronized修饰。(如果要保证线程安全的话就使用ConcurrentHashMap)
    1. 效率:因为线程安全的问题,HashMap要比HashTable效率高一点。另外HashTable基本被淘汰,不要在代码中使用它
    1. 对Null key和Null value的支持:HashMap中null可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为null。但是在HashTable中put进的键值只要有一个null,直接抛NullPointerException
    1. 初始容量大小和每次扩充容量大小的不同 :
  •  创建时如果不指定容量初始值,Hashtable默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。创建时如果给定了容量初始值,那么Hashtable会直接使用给定的大小,而HashMap会将其扩充为2的幂次方大小。也就是说HashMap总是使用2的幂作为哈希表的大小。
    
    1. 底层数据结构:JDK1.8以后的HashMap在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认8)时,将链表转化为红黑树,以减少搜索时间。Hashtable没有这样的机制。
    1. 推荐使用:在Hashtable的类注释可以看到,Hashtable是保留类不建议使用,推荐在单线程环境下使用HashMap替代,如果需要多线程使用则用ConcurrentHashMap替代。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值