记录了HashMap也来看看Hashtable吧,最近打算换份实习,所以想看看书回顾一下,不然就快记不得了.....囧啊囧啊,记性太差怎么破???
Hashtable里面的一些变量:
Entry<?,?>[] table : HashTable采用"拉链法"实现哈希表,每一个Entry代表了一个键值对(key-value)。
transient int count:hashtable里面键值对的个数;
private int threshold:hashtable的阀值(threshols = capacity * loadFactor);
float loadFactor:加载因子;
transient int modCount :hashtable被修改的次数,用来实现fast-fail机制。fast-fail机制就是在并发集合中,其进行迭代操作时,若有其他线程对其进行结构性的修改,这时迭代器会立马感知到,并且立即抛出 ConcurrentModificationException异常。
Hashtable的定义如下:
1 public class Hashtable<K,V> 2 extends Dictionary<K,V> 3 implements Map<K,V>, Cloneable, java.io.Serializable { 4 ... 5 }
可以看出Hashtable实现了Map接口,同时继承了Dictionary类。Map接口是将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。Dictionary
类是任何可将键映射到相应值的类(如 Hashtable
)的抽象父类。每个键和每个值都是一个对象。在任何一个 Dictionary 对象中,每个键至多与一个值相关联。给定一个 Dictionary 和一个键,就可以查找所关联的元素。任何非 null
对象都可以用作键或值。
看看Hashtable常用的构造函数的实现:
默认的构造函数:容量为11,加载因子为0.75
1 public Hashtable() { 2 this(11, 0.75f); 3 }
用指定的初始容量和默认的加载因子构造:
1 public Hashtable(int initialCapacity) { 2 this(initialCapacity, 0.75f); 3 }
用指定的初始容量和指定的加载因子构造:
1 public Hashtable(int initialCapacity, float loadFactor) { 2 // 参数合法性验证 3 if (initialCapacity < 0) 4 throw new IllegalArgumentException("Illegal Capacity: "+ 5 initialCapacity); 6 if (loadFactor <= 0 || Float.isNaN(loadFactor)) 7 throw new IllegalArgumentException("Illegal Load: "+loadFactor); 8 9 if (initialCapacity==0) 10 initialCapacity = 1; 11 this.loadFactor = loadFactor; 12 //用指定容量初始化table数组 13 table = new Entry<?,?>[initialCapacity]; 14 //计算table的阀值 15 threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1); 16 }
再来看看Put方法和get方法:
首先是put方法,put方法的整个处理流程是:计算key的hash值,根据hash值获得key在table数组中的索引位置,然后迭代该key处的Entry链表(我们暂且理解为链表),若该链表中存在一个这个的key对象,那么就直接替换其value值即可,否则在将改key-value节点插入该index索引位置处。注意,hashtable是不允许null值的。
int index = (hash & 0x7FFFFFFF) % tab.length——>键的hash值可能为负数,通过一个“与”操作,先将hash值都转化正数,然后将得到的hash值与Hashtable的长度求余
1 //同步实现 2 public synchronized V put(K key, V value) { 3 // check value值是否为null 4 if (value == null) { 5 throw new NullPointerException(); 6 } 7 8 // Makes sure the key is not already in the hashtable. 9 Entry<?,?> tab[] = table; 10 //计算key的hash值 11 int hash = key.hashCode(); 12 //通过hash值来计算在table数组中的下表 13 int index = (hash & 0x7FFFFFFF) % tab.length; 14 @SuppressWarnings("unchecked") 15 Entry<K,V> entry = (Entry<K,V>)tab[index]; 16 //遍历,寻找该key,找到则替换旧值 17 for(; entry != null ; entry = entry.next) { 18 if ((entry.hash == hash) && entry.key.equals(key)) { 19 V old = entry.value; 20 entry.value = value; 21 return old; 22 } 23 } 24 //没有找到,则将该key加入 25 addEntry(hash, key, value, index); 26 return null; 27 } 28 private void addEntry(int hash, K key, V value, int index) { 29 //修改链表,modCount自增 30 modCount++; 31 32 Entry<?,?> tab[] = table; 33 if (count >= threshold) { 34 // Rehash the table if the threshold is exceeded 35 rehash(); 36 tab = table; 37 hash = key.hashCode(); 38 index = (hash & 0x7FFFFFFF) % tab.length; 39 } 40 41 // Creates the new entry. 42 @SuppressWarnings("unchecked") 43 Entry<K,V> e = (Entry<K,V>) tab[index]; 44 tab[index] = new Entry<>(hash, key, value, e); 45 count++; 46 }
get方法也是同步的,具体的实现如下:
1 public synchronized V get(Object key) { 2 Entry<?,?> tab[] = table; 3 //计算key的hash值 4 int hash = key.hashCode(); 5 //通过hash值来计算在table数组中的下表 6 int index = (hash & 0x7FFFFFFF) % tab.length; 7 //遍历查找key,找到则返回key值所对应的的value 8 for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) { 9 if ((e.hash == hash) && e.key.equals(key)) { 10 return (V)e.value; 11 } 12 } 13 return null; 14 }