Java容器四:Hashtable

来源:互联网 发布:温什么知什么的成语 编辑:程序博客网 时间:2024/05/21 15:47

Hashtable

Map, Cloneable, Serializable
–>Dictionary


本文中的Hashtable主要与HashMap进行对比,其中有两个主要的区别,一是Hashtable中提供的公共方法中都由synchronized修饰,因此是保证线程安全的,而HashMap并未保证线程安全性。但Hashtable仍具有fail-fast特性,在得到iterator之后,若通过该迭代器之外的方式修改了表结构,再次调用该迭代器的方法时会抛出ConcurrentModificationException。第二是Hashtable的key值和value值不能为null(1.8中的方法仅要求value不能为null),而HashMap是可以为null的。在Hashtable中也使用桶这一概念,并且出现hash冲突时使用链表解决冲突。

初始化默认值

Hashtable的初始化默认值是直接写死在构造函数中的。

//  桶的最大大小,减8是因为很多时候数组还需要一部分空间存储本身的信息private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;public Hashtable() {    this(11, 0.75f);}public Hashtable(int initialCapacity) {    this(initialCapacity, 0.75f);}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);}

若参数initialCapacity输入0,则初始化桶大小为1。和HashMap等容器不同,Hashtable是在初始化的时候就给table分配了内存空间。

成员变量

// table中的每一个元素代表一个桶private transient Entry<?,?>[] table;// 已储存的元素个数private transient int count;// 定义为(capacity(table.size) * loadFactor),count超过该值即重新分配空间private int threshold;// 负载(默认值或赋新值)private float loadFactor;// 实现fail-fastprivate transient int modCount = 0;

桶定位和扩容方式

桶定位

Hashtable中根据key值计算hash值的方法为直接调用Object.hashCode计算。但计算桶标号的方法和HashMap不同,是使用了hash值的后31位与桶的数量取了一个模值运算。

Entry<?,?> tab[] = table;int hash = key.hashCode();int index = (hash & 0x7FFFFFFF) % tab.length;

扩容

HashMap的直接扩大两倍不同,Hashtable是将桶数量扩大为当前数量的两倍再加上1,保证桶数量是奇数(最好是保证是素数,但不太好实现,因此仅保证了奇数条件)。

protected void rehash() {    int oldCapacity = table.length;    Entry<?,?>[] oldMap = table;    // overflow-conscious code    int newCapacity = (oldCapacity << 1) + 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<?,?>[] newMap = new Entry<?,?>[newCapacity];    modCount++;    threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);    table = newMap;    for (int i = oldCapacity ; i-- > 0 ;) {        for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {            Entry<K,V> e = old;            old = old.next;            int index = (e.hash & 0x7FFFFFFF) % newCapacity;            e.next = (Entry<K,V>)newMap[index];            newMap[index] = e;        }    }}

entrySet,keySet,Values

得到这三个Set的方法类似,都是通过调用Collections.synchronizedCollection方法实现,将当前Hashtable对象作为锁,保证使用这三个Set时的线程安全问题。

private transient volatile Set<K> keySet;private transient volatile Set<Map.Entry<K,V>> entrySet;private transient volatile Collection<V> values;keySet = Collections.synchronizedSet(new KeySet(), this);entrySet = Collections.synchronizedSet(new EntrySet(),this);values = Collections.synchronizedCollection(new ValueCollection(),this);

枚举器,迭代器

Hashtable中得到枚举器和迭代器都是通过Enumerator类实现,可以看到,Enumerator实现了枚举器和迭代器两个接口,并通过iterator标识确认返回的是枚举器还是迭代器。同时,通过type确定需要返回的是key,value或entry的枚举器/迭代器。

// Types of Enumerations/Iterationsprivate static final int KEYS = 0;private static final int VALUES = 1;private static final int ENTRIES = 2;private class Enumerator<T> implements Enumeration<T>, Iterator<T>{Entry<?,?>[] table = Hashtable.this.table;int index = table.length;Entry<?,?> entry;Entry<?,?> lastReturned;int type;/** * Indicates whether this Enumerator is serving as an Iterator * or an Enumeration.  (true -> Iterator). */boolean iterator;/** * The modCount value that the iterator believes that the backing * Hashtable should have.  If this expectation is violated, the iterator * has detected concurrent modification. */protected int expectedModCount = modCount;}
0 0
原创粉丝点击