Java 集合系列 11 之 Hashtable 详细介绍 (源码解析) 和使用示例

Java 集合系列 11 之 Hashtable 详细介绍 (源码解析) 和使用示例

概要

前一章,我们学习了 HashMap。这一章,我们对 Hashtable 进行学习。
我们先对 Hashtable 有个整体认识,然后再学习它的源码,最后再通过实例来学会使用 Hashtable。
第 1 部分 Hashtable 介绍
第 2 部分 Hashtable 数据结构
第 3 部分 Hashtable 源码解析 (基于 JDK1.6.0_45)
第 4 部分 Hashtable 遍历方式
第 5 部分 Hashtable 示例

转载请注明出处:http://www.cnblogs.com/skywang12345/p/3310887.html

第 1 部分 Hashtable 介绍

Hashtable 简介

HashMap 一样,Hashtable 也是一个散列表,它存储的内容是键值对 (key-value) 映射
Hashtable 继承于 Dictionary,实现了 Map、Cloneable、java.io.Serializable 接口。
Hashtable 的函数都是同步的,这意味着它是线程安全的。它的 key、value 都不可以为 null。此外,Hashtable 中的映射不是有序的。

Hashtable 的实例有两个参数影响其性能:初始容量加载因子。容量 是哈希表中桶 的数量,初始容量 就是哈希表创建时的容量。注意,哈希表的状态为 open:在发生 “哈希冲突” 的情况下,单个桶会存储多个条目,这些条目必须按顺序搜索。加载因子 是对哈希表在其容量自动增加之前可以达到多满的一个尺度。初始容量和加载因子这两个参数只是对该实现的提示。关于何时以及是否调用 rehash 方法的具体细节则依赖于该实现。
通常,默认加载因子是 0.75, 这是在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查找某个条目的时间(在大多数 Hashtable 操作中,包括 get 和 put 操作,都反映了这一点)。

Hashtable 的构造函数

[img](javascript:void(0)😉

// 默认构造函数。
public Hashtable() 

// 指定“容量大小”的构造函数
public Hashtable(int initialCapacity) 

// 指定“容量大小”和“加载因子”的构造函数
public Hashtable(int initialCapacity, float loadFactor) 

// 包含“子Map”的构造函数
public Hashtable(Map<? extends K, ? extends V> t)

[img](javascript:void(0)😉

Hashtable 的 API

[img](javascript:void(0)😉

synchronized void                clear()
synchronized Object              clone()
             boolean             contains(Object value)
synchronized boolean             containsKey(Object key)
synchronized boolean             containsValue(Object value)
synchronized Enumeration<V>      elements()
synchronized Set<Entry<K, V>>    entrySet()
synchronized boolean             equals(Object object)
synchronized V                   get(Object key)
synchronized int                 hashCode()
synchronized boolean             isEmpty()
synchronized Set<K>              keySet()
synchronized Enumeration<K>      keys()
synchronized V                   put(K key, V value)
synchronized void                putAll(Map<? extends K, ? extends V> map)
synchronized V                   remove(Object key)
synchronized int                 size()
synchronized String              toString()
synchronized Collection<V>       values()

[img](javascript:void(0)😉

第 2 部分 Hashtable 数据结构

Hashtable 的继承关系

java.lang.Object
   ↳     java.util.Dictionary<K, V>
         ↳     java.util.Hashtable<K, V>

public class Hashtable<K,V> extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable { }

Hashtable 与 Map 关系如下图:

img

从图中可以看出:
(01) Hashtable 继承于 Dictionary 类,实现了 Map 接口。Map 是 "key-value 键值对" 接口,Dictionary 是声明了操作 "键值对" 函数接口的抽象类。
(02) Hashtable 是通过 "拉链法" 实现的哈希表。它包括几个重要的成员变量:table, count, threshold, loadFactor, modCount。
  table 是一个 Entry[] 数组类型,而 Entry 实际上就是一个单向链表。哈希表的 "key-value 键值对" 都是存储在 Entry 数组中的。
  count 是 Hashtable 的大小,它是 Hashtable 保存的键值对的数量。
  threshold 是 Hashtable 的阈值,用于判断是否需要调整 Hashtable 的容量。threshold 的值 ="容量 * 加载因子"。
  loadFactor 就是加载因子。
  modCount 是用来实现 fail-fast 机制的

第 3 部分 Hashtable 源码解析 (基于 JDK1.6.0_45)

为了更了解 Hashtable 的原理,下面对 Hashtable 源码代码作出分析。
在阅读源码时,建议参考后面的说明来建立对 Hashtable 的整体认识,这样更容易理解 Hashtable。

img

img

1 package java.util;
  2 import java.io.*;
  4 public class Hashtable<K,V>
  5     extends Dictionary<K,V>
  6     implements Map<K,V>, Cloneable, java.io.Serializable {
  8     // Hashtable保存key-value的数组。
  9     // Hashtable是采用拉链法实现的,每一个Entry本质上是一个单向链表
 10     private transient Entry[] table;
 12     // Hashtable中元素的实际数量
 13     private transient int count;
 15     // 阈值,用于判断是否需要调整Hashtable的容量(threshold = 容量*加载因子)
 16     private int threshold;
 18     // 加载因子
 19     private float loadFactor;
 21     // Hashtable被改变的次数
 22     private transient int modCount = 0;
 24     // 序列版本号
 25     private static final long serialVersionUID = 1421746759512286392L;
 27     // 指定“容量大小”和“加载因子”的构造函数
 28     public Hashtable(int initialCapacity, float loadFactor) {
 29         if (initialCapacity < 0)
 30             throw new IllegalArgumentException("Illegal Capacity: "+
 31                                                initialCapacity);
 32         if (loadFactor <= 0 || Float.isNaN(loadFactor))
 33             throw new IllegalArgumentException("Illegal Load: "+loadFactor);
 35         if (initialCapacity==0)
 36             initialCapacity = 1;
 37         this.loadFactor = loadFactor;
 38         table = new Entry[initialCapacity];
 39         threshold = (int)(initialCapacity * loadFactor);
 40     }
 42     // 指定“容量大小”的构造函数
 43     public Hashtable(int initialCapacity) {
 44         this(initialCapacity, 0.75f);
 45     }
 47     // 默认构造函数。
 48     public Hashtable() {
 49         // 默认构造函数,指定的容量大小是11;加载因子是0.75
 50         this(11, 0.75f);
 51     }
 53     // 包含“子Map”的构造函数
 54     public Hashtable(Map<? extends K, ? extends V> t) {
 55         this(Math.max(2*t.size(), 11), 0.75f);
 56         // 将“子Map”的全部元素都添加到Hashtable中
 57         putAll(t);
 58     }
 60     public synchronized int size() {
 61         return count;
 62     }
 64     public synchronized boolean isEmpty() {
 65         return count == 0;
 66     }
 68     // 返回“所有key”的枚举对象
 69     public synchronized Enumeration<K> keys() {
 70         return this.<K>getEnumeration(KEYS);
 71     }
 73     // 返回“所有value”的枚举对象
 74     public synchronized Enumeration<V> elements() {
 75         return this.<V>getEnumeration(VALUES);
 76     }
 78     // 判断Hashtable是否包含“值(value)”
 79     public synchronized boolean contains(Object value) {
 80         // Hashtable中“键值对”的value不能是null,
 81         // 若是null的话,抛出异常!
 82         if (value == null) {
 83             throw new NullPointerException();
 84         }
 86         // 从后向前遍历table数组中的元素(Entry)
 87         // 对于每个Entry(单向链表),逐个遍历,判断节点的值是否等于value
 88         Entry tab[] = table;
 89         for (int i = tab.length ; i-- > 0 ;) {
 90             for (Entry<K,V> e = tab[i] ; e != null ; e = e.next) {
 91                 if (e.value.equals(value)) {
 92                     return true;
 93                 }
 94             }
 95         }
 96         return false;
 97     }
 99     public boolean containsValue(Object value) {
100         return contains(value);
101     }
103     // 判断Hashtable是否包含key
104     public synchronized boolean containsKey(Object key) {
105         Entry tab[] = table;
106         int hash = key.hashCode();
107         // 计算索引值,
108         // % tab.length 的目的是防止数据越界
109         int index = (hash & 0x7FFFFFFF) % tab.length;
110         // 找到“key对应的Entry(链表)”,然后在链表中找出“哈希值”和“键值”与key都相等的元素
111         for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
112             if ((e.hash == hash) && e.key.equals(key)) {
113                 return true;
114             }
115         }
116         return false;
117     }
119     // 返回key对应的value,没有的话返回null
120     public synchronized V get(Object key) {
121         Entry tab[] = table;
122         int hash = key.hashCode();
123         // 计算索引值,
124         int index = (hash & 0x7FFFFFFF) % tab.length;
125         // 找到“key对应的Entry(链表)”,然后在链表中找出“哈希值”和“键值”与key都相等的元素
126         for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
127             if ((e.hash == hash) && e.key.equals(key)) {
128                 return e.value;
129             }
130         }
131         return null;
132     }
134     // 调整Hashtable的长度,将长度变成原来的(2倍+1)
135     // (01) 将“旧的Entry数组”赋值给一个临时变量。
136     // (02) 创建一个“新的Entry数组”,并赋值给“旧的Entry数组”
137     // (03) 将“Hashtable”中的全部元素依次添加到“新的Entry数组”中
138     protected void rehash() {
139         int oldCapacity = table.length;
140         Entry[] oldMap = table;
142         int newCapacity = oldCapacity * 2 + 1;
143         Entry[] newMap = new Entry[newCapacity];
145         modCount++;
146         threshold = (int)(newCapacity * loadFactor);
147         table = newMap;
149         for (int i = oldCapacity ; i-- > 0 ;) {
150             for (Entry<K,V> old = oldMap[i] ; old != null ; ) {
151                 Entry<K,V> e = old;
152                 old = old.next;
154                 int index = (e.hash & 0x7FFFFFFF) % newCapacity;
155                 e.next = newMap[index];
156                 newMap[index] = e;
157             }
158         }
159     }
161     // 将“key-value”添加到Hashtable中
162     public synchronized V put(K key, V value) {
163         // Hashtable中不能插入value为null的元素!!!
164         if (value == null) {
165             throw new NullPointerException();
166         }
168         // 若“Hashtable中已存在键为key的键值对”,
169         // 则用“新的value”替换“旧的value”
170         Entry tab[] = table;
171         int hash = key.hashCode();
172         int index = (hash & 0x7FFFFFFF) % tab.length;
173         for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
174             if ((e.hash == hash) && e.key.equals(key)) {
175                 V old = e.value;
176                 e.value = value;
177                 return old;
178                 }
179         }
181         // 若“Hashtable中不存在键为key的键值对”,
182         // (01) 将“修改统计数”+1
183         modCount++;
184         // (02) 若“Hashtable实际容量” > “阈值”(阈值=总的容量 * 加载因子)
185         //  则调整Hashtable的大小
186         if (count >= threshold) {
187             // Rehash the table if the threshold is exceeded
188             rehash();
190             tab = table;
191             index = (hash & 0x7FFFFFFF) % tab.length;
192         }
194         // (03) 将“Hashtable中index”位置的Entry(链表)保存到e中
195         Entry<K,V> e = tab[index];
196         // (04) 创建“新的Entry节点”,并将“新的Entry”插入“Hashtable的index位置”,并设置e为“新的Entry”的下一个元素(即“新Entry”为链表表头)。        
197         tab[index] = new Entry<K,V>(hash, key, value, e);
198         // (05) 将“Hashtable的实际容量”+1
199         count++;
200         return null;
201     }
203     // 删除Hashtable中键为key的元素
204     public synchronized V remove(Object key) {
205         Entry tab[] = table;
206         int hash = key.hashCode();
207         int index = (hash & 0x7FFFFFFF) % tab.length;
208         // 找到“key对应的Entry(链表)”
209         // 然后在链表中找出要删除的节点,并删除该节点。
210         for (Entry<K,V> e = tab[index], prev = null ; e != null ; prev = e, e = e.next) {
211             if ((e.hash == hash) && e.key.equals(key)) {
212                 modCount++;
213                 if (prev != null) {
214                     prev.next = e.next;
215                 } else {
216                     tab[index] = e.next;
217                 }
218                 count--;
219                 V oldValue = e.value;
220                 e.value = null;
221                 return oldValue;
222             }
223         }
224         return null;
225     }
227     // 将“Map(t)”的中全部元素逐一添加到Hashtable中
228     public synchronized void putAll(Map<? extends K, ? extends V> t) {
229         for (Map.Entry<? extends K, ? extends V> e : t.entrySet())
230             put(e.getKey(), e.getValue());
231     }
233     // 清空Hashtable
234     // 将Hashtable的table数组的值全部设为null
235     public synchronized void clear() {
236         Entry tab[] = table;
237         modCount++;
238         for (int index = tab.length; --index >= 0; )
239             tab[index] = null;
240         count = 0;
241     }
243     // 克隆一个Hashtable,并以Object的形式返回。
244     public synchronized Object clone() {
245         try {
246             Hashtable<K,V> t = (Hashtable<K,V>) super.clone();
247             t.table = new Entry[table.length];
248             for (int i = table.length ; i-- > 0 ; ) {
249                 t.table[i] = (table[i] != null)
250                 ? (Entry<K,V>) table[i].clone() : null;
251             }
252             t.keySet = null;
253             t.entrySet = null;
254             t.values = null;
255             t.modCount = 0;
256             return t;
257         } catch (CloneNotSupportedException e) {
258             // this shouldn't happen, since we are Cloneable
259             throw new InternalError();
260         }
261     }
263     public synchronized String toString() {
264         int max = size() - 1;
265         if (max == -1)
266             return "{}";
268         StringBuilder sb = new StringBuilder();
269         Iterator<Map.Entry<K,V>> it = entrySet().iterator();
271         sb.append('{');
272         for (int i = 0; ; i++) {
273             Map.Entry<K,V> e = it.next();
274             K key = e.getKey();
275             V value = e.getValue();
276             sb.append(key   == this ? "(this Map)" : key.toString());
277             sb.append('=');
278             sb.append(value == this ? "(this Map)" : value.toString());
280             if (i == max)
281                 return sb.append('}').toString();
282             sb.append(", ");
283         }
284     }
286     // 获取Hashtable的枚举类对象
287     // 若Hashtable的实际大小为0,则返回“空枚举类”对象;
288     // 否则,返回正常的Enumerator的对象。(Enumerator实现了迭代器和枚举两个接口)
289     private <T> Enumeration<T> getEnumeration(int type) {
290     if (count == 0) {
291         return (Enumeration<T>)emptyEnumerator;
292     } else {
293         return new Enumerator<T>(type, false);
294     }
295     }
297     // 获取Hashtable的迭代器
298     // 若Hashtable的实际大小为0,则返回“空迭代器”对象;
299     // 否则,返回正常的Enumerator的对象。(Enumerator实现了迭代器和枚举两个接口)
300     private <T> Iterator<T> getIterator(int type) {
301         if (count == 0) {
302             return (Iterator<T>) emptyIterator;
303         } else {
304             return new Enumerator<T>(type, true);
305         }
306     }
308     // Hashtable的“key的集合”。它是一个Set,意味着没有重复元素
309     private transient volatile Set<K> keySet = null;
310     // Hashtable的“key-value的集合”。它是一个Set,意味着没有重复元素
311     private transient volatile Set<Map.Entry<K,V>> entrySet = null;
312     // Hashtable的“key-value的集合”。它是一个Collection,意味着可以有重复元素
313     private transient volatile Collection<V> values = null;
315     // 返回一个被synchronizedSet封装后的KeySet对象
316     // synchronizedSet封装的目的是对KeySet的所有方法都添加synchronized,实现多线程同步
317     public Set<K> keySet() {
318         if (keySet == null)
319             keySet = Collections.synchronizedSet(new KeySet(), this);
320         return keySet;
321     }
323     // Hashtable的Key的Set集合。
324     // KeySet继承于AbstractSet,所以,KeySet中的元素没有重复的。
325     private class KeySet extends AbstractSet<K> {
326         public Iterator<K> iterator() {
327             return getIterator(KEYS);
328         }
329         public int size() {
330             return count;
331         }
332         public boolean contains(Object o) {
333             return containsKey(o);
334         }
335         public boolean remove(Object o) {
336             return Hashtable.this.remove(o) != null;
337         }
338         public void clear() {
339             Hashtable.this.clear();
340         }
341     }
343     // 返回一个被synchronizedSet封装后的EntrySet对象
344     // synchronizedSet封装的目的是对EntrySet的所有方法都添加synchronized,实现多线程同步
345     public Set<Map.Entry<K,V>> entrySet() {
346         if (entrySet==null)
347             entrySet = Collections.synchronizedSet(new EntrySet(), this);
348         return entrySet;
349     }
351     // Hashtable的Entry的Set集合。
352     // EntrySet继承于AbstractSet,所以,EntrySet中的元素没有重复的。
353     private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
354         public Iterator<Map.Entry<K,V>> iterator() {
355             return getIterator(ENTRIES);
356         }
358         public boolean add(Map.Entry<K,V> o) {
359             return super.add(o);
360         }
362         // 查找EntrySet中是否包含Object(0)
363         // 首先,在table中找到o对应的Entry(Entry是一个单向链表)
364         // 然后,查找Entry链表中是否存在Object
365         public boolean contains(Object o) {
366             if (!(o instanceof Map.Entry))
367                 return false;
368             Map.Entry entry = (Map.Entry)o;
369             Object key = entry.getKey();
370             Entry[] tab = table;
371             int hash = key.hashCode();
372             int index = (hash & 0x7FFFFFFF) % tab.length;
374             for (Entry e = tab[index]; e != null; e = e.next)
375                 if (e.hash==hash && e.equals(entry))
376                     return true;
377             return false;
378         }
380         // 删除元素Object(0)
381         // 首先,在table中找到o对应的Entry(Entry是一个单向链表)
382         // 然后,删除链表中的元素Object
383         public boolean remove(Object o) {
384             if (!(o instanceof Map.Entry))
385                 return false;
386             Map.Entry<K,V> entry = (Map.Entry<K,V>) o;
387             K key = entry.getKey();
388             Entry[] tab = table;
389             int hash = key.hashCode();
390             int index = (hash & 0x7FFFFFFF) % tab.length;
392             for (Entry<K,V> e = tab[index], prev = null; e != null;
393                  prev = e, e = e.next) {
394                 if (e.hash==hash && e.equals(entry)) {
395                     modCount++;
396                     if (prev != null)
397                         prev.next = e.next;
398                     else
399                         tab[index] = e.next;
401                     count--;
402                     e.value = null;
403                     return true;
404                 }
405             }
406             return false;
407         }
409         public int size() {
410             return count;
411         }
413         public void clear() {
414             Hashtable.this.clear();
415         }
416     }
418     // 返回一个被synchronizedCollection封装后的ValueCollection对象
419     // synchronizedCollection封装的目的是对ValueCollection的所有方法都添加synchronized,实现多线程同步
420     public Collection<V> values() {
421     if (values==null)
422         values = Collections.synchronizedCollection(new ValueCollection(),
423                                                         this);
424         return values;
425     }
427     // Hashtable的value的Collection集合。
428     // ValueCollection继承于AbstractCollection,所以,ValueCollection中的元素可以重复的。
429     private class ValueCollection extends AbstractCollection<V> {
430         public Iterator<V> iterator() {
431         return getIterator(VALUES);
432         }
433         public int size() {
434             return count;
435         }
436         public boolean contains(Object o) {
437             return containsValue(o);
438         }
439         public void clear() {
440             Hashtable.this.clear();
441         }
442     }
444     // 重新equals()函数
445     // 若两个Hashtable的所有key-value键值对都相等,则判断它们两个相等
446     public synchronized boolean equals(Object o) {
447         if (o == this)
448             return true;
450         if (!(o instanceof Map))
451             return false;
452         Map<K,V> t = (Map<K,V>) o;
453         if (t.size() != size())
454             return false;
456         try {
457             // 通过迭代器依次取出当前Hashtable的key-value键值对
458             // 并判断该键值对,存在于Hashtable(o)中。
459             // 若不存在,则立即返回false;否则,遍历完“当前Hashtable”并返回true。
460             Iterator<Map.Entry<K,V>> i = entrySet().iterator();
461             while (i.hasNext()) {
462                 Map.Entry<K,V> e = i.next();
463                 K key = e.getKey();
464                 V value = e.getValue();
465                 if (value == null) {
466                     if (!(t.get(key)==null && t.containsKey(key)))
467                         return false;
468                 } else {
469                     if (!value.equals(t.get(key)))
470                         return false;
471                 }
472             }
473         } catch (ClassCastException unused)   {
474             return false;
475         } catch (NullPointerException unused) {
476             return false;
477         }
479         return true;
480     }
482     // 计算Hashtable的哈希值
483     // 若 Hashtable的实际大小为0 或者 加载因子<0,则返回0。
484     // 否则,返回“Hashtable中的每个Entry的key和value的异或值 的总和”。
485     public synchronized int hashCode() {
486         int h = 0;
487         if (count == 0 || loadFactor < 0)
488             return h;  // Returns zero
490         loadFactor = -loadFactor;  // Mark hashCode computation in progress
491         Entry[] tab = table;
492         for (int i = 0; i < tab.length; i++)
493             for (Entry e = tab[i]; e != null; e = e.next)
494                 h += e.key.hashCode() ^ e.value.hashCode();
495         loadFactor = -loadFactor;  // Mark hashCode computation complete
497         return h;
498     }
500     // java.io.Serializable的写入函数
501     // 将Hashtable的“总的容量,实际容量,所有的Entry”都写入到输出流中
502     private synchronized void writeObject(java.io.ObjectOutputStream s)
503         throws IOException
504     {
505         // Write out the length, threshold, loadfactor
506         s.defaultWriteObject();
508         // Write out length, count of elements and then the key/value objects
509         s.writeInt(table.length);
510         s.writeInt(count);
511         for (int index = table.length-1; index >= 0; index--) {
512             Entry entry = table[index];
514             while (entry != null) {
515             s.writeObject(entry.key);
516             s.writeObject(entry.value);
517             entry = entry.next;
518             }
519         }
520     }
522     // java.io.Serializable的读取函数:根据写入方式读出
523     // 将Hashtable的“总的容量,实际容量,所有的Entry”依次读出
524     private void readObject(java.io.ObjectInputStream s)
525          throws IOException, ClassNotFoundException
526     {
527         // Read in the length, threshold, and loadfactor
528         s.defaultReadObject();
530         // Read the original length of the array and number of elements
531         int origlength = s.readInt();
532         int elements = s.readInt();
534         // Compute new size with a bit of room 5% to grow but
535         // no larger than the original size.  Make the length
536         // odd if it's large enough, this helps distribute the entries.
537         // Guard against the length ending up zero, that's not valid.
538         int length = (int)(elements * loadFactor) + (elements / 20) + 3;
539         if (length > elements && (length & 1) == 0)
540             length--;
541         if (origlength > 0 && length > origlength)
542             length = origlength;
544         Entry[] table = new Entry[length];
545         count = 0;
547         // Read the number of elements and then all the key/value objects
548         for (; elements > 0; elements--) {
549             K key = (K)s.readObject();
550             V value = (V)s.readObject();
551                 // synch could be eliminated for performance
552                 reconstitutionPut(table, key, value);
553         }
554         this.table = table;
555     }
557     private void reconstitutionPut(Entry[] tab, K key, V value)
558         throws StreamCorruptedException
559     {
560         if (value == null) {
561             throw new java.io.StreamCorruptedException();
562         }
563         // Makes sure the key is not already in the hashtable.
564         // This should not happen in deserialized version.
565         int hash = key.hashCode();
566         int index = (hash & 0x7FFFFFFF) % tab.length;
567         for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
568             if ((e.hash == hash) && e.key.equals(key)) {
569                 throw new java.io.StreamCorruptedException();
570             }
571         }
572         // Creates the new entry.
573         Entry<K,V> e = tab[index];
574         tab[index] = new Entry<K,V>(hash, key, value, e);
575         count++;
576     }
578     // Hashtable的Entry节点,它本质上是一个单向链表。
579     // 也因此,我们才能推断出Hashtable是由拉链法实现的散列表
580     private static class Entry<K,V> implements Map.Entry<K,V> {
581         // 哈希值
582         int hash;
583         K key;
584         V value;
585         // 指向的下一个Entry,即链表的下一个节点
586         Entry<K,V> next;
588         // 构造函数
589         protected Entry(int hash, K key, V value, Entry<K,V> next) {
590             this.hash = hash;
591             this.key = key;
592             this.value = value;
593             this.next = next;
594         }
596         protected Object clone() {
597             return new Entry<K,V>(hash, key, value,
598                   (next==null ? null : (Entry<K,V>) next.clone()));
599         }
601         public K getKey() {
602             return key;
603         }
605         public V getValue() {
606             return value;
607         }
609         // 设置value。若value是null,则抛出异常。
610         public V setValue(V value) {
611             if (value == null)
612                 throw new NullPointerException();
614             V oldValue = this.value;
615             this.value = value;
616             return oldValue;
617         }
619         // 覆盖equals()方法,判断两个Entry是否相等。
620         // 若两个Entry的key和value都相等,则认为它们相等。
621         public boolean equals(Object o) {
622             if (!(o instanceof Map.Entry))
623                 return false;
624             Map.Entry e = (Map.Entry)o;
626             return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
627                (value==null ? e.getValue()==null : value.equals(e.getValue()));
628         }
630         public int hashCode() {
631             return hash ^ (value==null ? 0 : value.hashCode());
632         }
634         public String toString() {
635             return key.toString()+"="+value.toString();
636         }
637     }
639     private static final int KEYS = 0;
640     private static final int VALUES = 1;
641     private static final int ENTRIES = 2;
643     // Enumerator的作用是提供了“通过elements()遍历Hashtable的接口” 和 “通过entrySet()遍历Hashtable的接口”。因为,它同时实现了 “Enumerator接口”和“Iterator接口”。
644     private class Enumerator<T> implements Enumeration<T>, Iterator<T> {
645         // 指向Hashtable的table
646         Entry[] table = Hashtable.this.table;
647         // Hashtable的总的大小
648         int index = table.length;
649         Entry<K,V> entry = null;
650         Entry<K,V> lastReturned = null;
651         int type;
653         // Enumerator是 “迭代器(Iterator)” 还是 “枚举类(Enumeration)”的标志
654         // iterator为true,表示它是迭代器;否则,是枚举类。
655         boolean iterator;
657         // 在将Enumerator当作迭代器使用时会用到,用来实现fail-fast机制。
658         protected int expectedModCount = modCount;
660         Enumerator(int type, boolean iterator) {
661             this.type = type;
662             this.iterator = iterator;
663         }
665         // 从遍历table的数组的末尾向前查找,直到找到不为null的Entry。
666         public boolean hasMoreElements() {
667             Entry<K,V> e = entry;
668             int i = index;
669             Entry[] t = table;
670             /* Use locals for faster loop iteration */
671             while (e == null && i > 0) {
672                 e = t[--i];
673             }
674             entry = e;
675             index = i;
676             return e != null;
677         }
679         // 获取下一个元素
680         // 注意:从hasMoreElements() 和nextElement() 可以看出“Hashtable的elements()遍历方式”
681         // 首先,从后向前的遍历table数组。table数组的每个节点都是一个单向链表(Entry)。
682         // 然后,依次向后遍历单向链表Entry。
683         public T nextElement() {
684             Entry<K,V> et = entry;
685             int i = index;
686             Entry[] t = table;
687             /* Use locals for faster loop iteration */
688             while (et == null && i > 0) {
689                 et = t[--i];
690             }
691             entry = et;
692             index = i;
693             if (et != null) {
694                 Entry<K,V> e = lastReturned = entry;
695                 entry = e.next;
696                 return type == KEYS ? (T)e.key : (type == VALUES ? (T)e.value : (T)e);
697             }
698             throw new NoSuchElementException("Hashtable Enumerator");
699         }
701         // 迭代器Iterator的判断是否存在下一个元素
702         // 实际上,它是调用的hasMoreElements()
703         public boolean hasNext() {
704             return hasMoreElements();
705         }
707         // 迭代器获取下一个元素
708         // 实际上,它是调用的nextElement()
709         public T next() {
710             if (modCount != expectedModCount)
711                 throw new ConcurrentModificationException();
712             return nextElement();
713         }
715         // 迭代器的remove()接口。
716         // 首先,它在table数组中找出要删除元素所在的Entry,
717         // 然后,删除单向链表Entry中的元素。
718         public void remove() {
719             if (!iterator)
720                 throw new UnsupportedOperationException();
721             if (lastReturned == null)
722                 throw new IllegalStateException("Hashtable Enumerator");
723             if (modCount != expectedModCount)
724                 throw new ConcurrentModificationException();
726             synchronized(Hashtable.this) {
727                 Entry[] tab = Hashtable.this.table;
728                 int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;
730                 for (Entry<K,V> e = tab[index], prev = null; e != null;
731                      prev = e, e = e.next) {
732                     if (e == lastReturned) {
733                         modCount++;
734                         expectedModCount++;
735                         if (prev == null)
736                             tab[index] = e.next;
737                         else
738                             prev.next = e.next;
739                         count--;
740                         lastReturned = null;
741                         return;
742                     }
743                 }
744                 throw new ConcurrentModificationException();
745             }
746         }
747     }
750     private static Enumeration emptyEnumerator = new EmptyEnumerator();
751     private static Iterator emptyIterator = new EmptyIterator();
753     // 空枚举类
754     // 当Hashtable的实际大小为0;此时,又要通过Enumeration遍历Hashtable时,返回的是“空枚举类”的对象。
755     private static class EmptyEnumerator implements Enumeration<Object> {
757         EmptyEnumerator() {
758         }
760         // 空枚举类的hasMoreElements() 始终返回false
761         public boolean hasMoreElements() {
762             return false;
763         }
765         // 空枚举类的nextElement() 抛出异常
766         public Object nextElement() {
767             throw new NoSuchElementException("Hashtable Enumerator");
768         }
769     }
772     // 空迭代器
773     // 当Hashtable的实际大小为0;此时,又要通过迭代器遍历Hashtable时,返回的是“空迭代器”的对象。
774     private static class EmptyIterator implements Iterator<Object> {
776         EmptyIterator() {
777         }
779         public boolean hasNext() {
780             return false;
781         }
783         public Object next() {
784             throw new NoSuchElementException("Hashtable Iterator");
785         }
787         public void remove() {
788             throw new IllegalStateException("Hashtable Iterator");
789         }
791     }
792 }

View Code

说明: 在详细介绍 Hashtable 的代码之前,我们需要了解:和 Hashmap 一样,Hashtable 也是一个散列表,它也是通过 “拉链法” 解决哈希冲突的。

第 3.1 部分 Hashtable 的 “拉链法” 相关内容

3.1.1 Hashtable 数据存储数组

private transient Entry[] table;

Hashtable 中的 key-value 都是存储在 table 数组中的

3.1.2 数据节点 Entry 的数据结构

img

img

1 private static class Entry<K,V> implements Map.Entry<K,V> {
 2     // 哈希值
 3     int hash;
 4     K key;
 5     V value;
 6     // 指向的下一个Entry,即链表的下一个节点
 7     Entry<K,V> next;
 9     // 构造函数
10     protected Entry(int hash, K key, V value, Entry<K,V> next) {
11         this.hash = hash;
12         this.key = key;
13         this.value = value;
14         this.next = next;
15     }
17     protected Object clone() {
18         return new Entry<K,V>(hash, key, value,
19               (next==null ? null : (Entry<K,V>) next.clone()));
20     }
22     public K getKey() {
23         return key;
24     }
26     public V getValue() {
27         return value;
28     }
30     // 设置value。若value是null,则抛出异常。
31     public V setValue(V value) {
32         if (value == null)
33             throw new NullPointerException();
35         V oldValue = this.value;
36         this.value = value;
37         return oldValue;
38     }
40     // 覆盖equals()方法,判断两个Entry是否相等。
41     // 若两个Entry的key和value都相等,则认为它们相等。
42     public boolean equals(Object o) {
43         if (!(o instanceof Map.Entry))
44             return false;
45         Map.Entry e = (Map.Entry)o;
47         return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
48            (value==null ? e.getValue()==null : value.equals(e.getValue()));
49     }
51     public int hashCode() {
52         return hash ^ (value==null ? 0 : value.hashCode());
53     }
55     public String toString() {
56         return key.toString()+"="+value.toString();
57     }
58 }

View Code

从中,我们可以看出 Entry 实际上就是一个单向链表。这也是为什么我们说 Hashtable 是通过拉链法解决哈希冲突的。
Entry 实现了 Map.Entry 接口,即实现 getKey(), getValue(), setValue(V value), equals(Object o), hashCode() 这些函数。这些都是基本的读取 / 修改 key、value 值的函数。

第 3.2 部分 Hashtable 的构造函数

Hashtable 共包括 4 个构造函数

img

img

1 // 默认构造函数。
 2 public Hashtable() {
 3     // 默认构造函数,指定的容量大小是11;加载因子是0.75
 4     this(11, 0.75f);
 5 }
 7 // 指定“容量大小”的构造函数
 8 public Hashtable(int initialCapacity) {
 9     this(initialCapacity, 0.75f);
10 }
12 // 指定“容量大小”和“加载因子”的构造函数
13 public Hashtable(int initialCapacity, float loadFactor) {
14     if (initialCapacity < 0)
15         throw new IllegalArgumentException("Illegal Capacity: "+
16                                            initialCapacity);
17     if (loadFactor <= 0 || Float.isNaN(loadFactor))
18         throw new IllegalArgumentException("Illegal Load: "+loadFactor);
20     if (initialCapacity==0)
21         initialCapacity = 1;
22     this.loadFactor = loadFactor;
23     table = new Entry[initialCapacity];
24     threshold = (int)(initialCapacity * loadFactor);
25 }
27 // 包含“子Map”的构造函数
28 public Hashtable(Map<? extends K, ? extends V> t) {
29     this(Math.max(2*t.size(), 11), 0.75f);
30     // 将“子Map”的全部元素都添加到Hashtable中
31     putAll(t);
32 }

View Code

第 3.3 部分 Hashtable 的主要对外接口

3.3.1 clear()

clear() 的作用是清空 Hashtable。它是将 Hashtable 的 table 数组的值全部设为 null

img

img

1 public synchronized void clear() {
2     Entry tab[] = table;
3     modCount++;
4     for (int index = tab.length; --index >= 0; )
5         tab[index] = null;
6     count = 0;
7 }

View Code

3.3.2 contains()containsValue()

contains() 和 containsValue() 的作用都是判断 Hashtable 是否包含 “值 (value)”

img

img

1 public boolean containsValue(Object value) {
 2     return contains(value);
 3 }
 5 public synchronized boolean contains(Object value) {
 6     // Hashtable中“键值对”的value不能是null,
 7     // 若是null的话,抛出异常!
 8     if (value == null) {
 9         throw new NullPointerException();
10     }
12     // 从后向前遍历table数组中的元素(Entry)
13     // 对于每个Entry(单向链表),逐个遍历,判断节点的值是否等于value
14     Entry tab[] = table;
15     for (int i = tab.length ; i-- > 0 ;) {
16         for (Entry<K,V> e = tab[i] ; e != null ; e = e.next) {
17             if (e.value.equals(value)) {
18                 return true;
19             }
20         }
21     }
22     return false;
23 }

View Code

3.3.3 containsKey()

containsKey() 的作用是判断 Hashtable 是否包含 key

img

img

1 public synchronized boolean containsKey(Object key) {
 2     Entry tab[] = table;
 3     int hash = key.hashCode();
 4     // 计算索引值,
 5     // % tab.length 的目的是防止数据越界
 6     int index = (hash & 0x7FFFFFFF) % tab.length;
 7     // 找到“key对应的Entry(链表)”,然后在链表中找出“哈希值”和“键值”与key都相等的元素
 8     for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
 9         if ((e.hash == hash) && e.key.equals(key)) {
10             return true;
11         }
12     }
13     return false;
14 }

View Code

3.3.4 elements()

elements() 的作用是返回 “所有 value” 的枚举对象

img

img

1 public synchronized Enumeration<V> elements() {
 2     return this.<V>getEnumeration(VALUES);
 3 }
 5 // 获取Hashtable的枚举类对象
 6 private <T> Enumeration<T> getEnumeration(int type) {
 7     if (count == 0) {
 8         return (Enumeration<T>)emptyEnumerator;
 9     } else {
10         return new Enumerator<T>(type, false);
11     }
12 }

View Code

从中,我们可以看出:
(01) 若 Hashtable 的实际大小为 0, 则返回 “空枚举类” 对象 emptyEnumerator;
(02) 否则,返回正常的 Enumerator 的对象。(Enumerator 实现了迭代器和枚举两个接口)

我们先看看 emptyEnumerator 对象是如何实现的

img

img

1 private static Enumeration emptyEnumerator = new EmptyEnumerator();
 3 // 空枚举类
 4 // 当Hashtable的实际大小为0;此时,又要通过Enumeration遍历Hashtable时,返回的是“空枚举类”的对象。
 5 private static class EmptyEnumerator implements Enumeration<Object> {
 7     EmptyEnumerator() {
 8     }
10     // 空枚举类的hasMoreElements() 始终返回false
11     public boolean hasMoreElements() {
12         return false;
13     }
15     // 空枚举类的nextElement() 抛出异常
16     public Object nextElement() {
17         throw new NoSuchElementException("Hashtable Enumerator");
18     }
19 }

View Code

我们在来看看 Enumeration 类

Enumerator 的作用是提供了 “通过 elements() 遍历 Hashtable 的接口” 和 “通过 entrySet()遍历 Hashtable 的接口”。因为,它同时实现了 “Enumerator 接口” 和 “Iterator 接口”。

img

img

1 private class Enumerator<T> implements Enumeration<T>, Iterator<T> {
  2     // 指向Hashtable的table
  3     Entry[] table = Hashtable.this.table;
  4     // Hashtable的总的大小
  5     int index = table.length;
  6     Entry<K,V> entry = null;
  7     Entry<K,V> lastReturned = null;
  8     int type;
 10     // Enumerator是 “迭代器(Iterator)” 还是 “枚举类(Enumeration)”的标志
 11     // iterator为true,表示它是迭代器;否则,是枚举类。
 12     boolean iterator;
 14     // 在将Enumerator当作迭代器使用时会用到,用来实现fail-fast机制。
 15     protected int expectedModCount = modCount;
 17     Enumerator(int type, boolean iterator) {
 18         this.type = type;
 19         this.iterator = iterator;
 20     }
 22     // 从遍历table的数组的末尾向前查找,直到找到不为null的Entry。
 23     public boolean hasMoreElements() {
 24         Entry<K,V> e = entry;
 25         int i = index;
 26         Entry[] t = table;
 27         /* Use locals for faster loop iteration */
 28         while (e == null && i > 0) {
 29             e = t[--i];
 30         }
 31         entry = e;
 32         index = i;
 33         return e != null;
 34     }
 36     // 获取下一个元素
 37     // 注意:从hasMoreElements() 和nextElement() 可以看出“Hashtable的elements()遍历方式”
 38     // 首先,从后向前的遍历table数组。table数组的每个节点都是一个单向链表(Entry)。
 39     // 然后,依次向后遍历单向链表Entry。
 40     public T nextElement() {
 41         Entry<K,V> et = entry;
 42         int i = index;
 43         Entry[] t = table;
 44         /* Use locals for faster loop iteration */
 45         while (et == null && i > 0) {
 46             et = t[--i];
 47         }
 48         entry = et;
 49         index = i;
 50         if (et != null) {
 51             Entry<K,V> e = lastReturned = entry;
 52             entry = e.next;
 53             return type == KEYS ? (T)e.key : (type == VALUES ? (T)e.value : (T)e);
 54         }
 55         throw new NoSuchElementException("Hashtable Enumerator");
 56     }
 58     // 迭代器Iterator的判断是否存在下一个元素
 59     // 实际上,它是调用的hasMoreElements()
 60     public boolean hasNext() {
 61         return hasMoreElements();
 62     }
 64     // 迭代器获取下一个元素
 65     // 实际上,它是调用的nextElement()
 66     public T next() {
 67         if (modCount != expectedModCount)
 68             throw new ConcurrentModificationException();
 69         return nextElement();
 70     }
 72     // 迭代器的remove()接口。
 73     // 首先,它在table数组中找出要删除元素所在的Entry,
 74     // 然后,删除单向链表Entry中的元素。
 75     public void remove() {
 76         if (!iterator)
 77             throw new UnsupportedOperationException();
 78         if (lastReturned == null)
 79             throw new IllegalStateException("Hashtable Enumerator");
 80         if (modCount != expectedModCount)
 81             throw new ConcurrentModificationException();
 83         synchronized(Hashtable.this) {
 84             Entry[] tab = Hashtable.this.table;
 85             int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;
 87             for (Entry<K,V> e = tab[index], prev = null; e != null;
 88                  prev = e, e = e.next) {
 89                 if (e == lastReturned) {
 90                     modCount++;
 91                     expectedModCount++;
 92                     if (prev == null)
 93                         tab[index] = e.next;
 94                     else
 95                         prev.next = e.next;
 96                     count--;
 97                     lastReturned = null;
 98                     return;
 99                 }
100             }
101             throw new ConcurrentModificationException();
102         }
103     }
104 }

View Code

entrySet(), keySet(), keys(), values() 的实现方法和 elements() 差不多,而且源码中已经明确的给出了注释。这里就不再做过多说明了。

3.3.5 get()

get() 的作用就是获取 key 对应的 value,没有的话返回 null

img

img

1 public synchronized V get(Object key) {
 2     Entry tab[] = table;
 3     int hash = key.hashCode();
 4     // 计算索引值,
 5     int index = (hash & 0x7FFFFFFF) % tab.length;
 6     // 找到“key对应的Entry(链表)”,然后在链表中找出“哈希值”和“键值”与key都相等的元素
 7     for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
 8         if ((e.hash == hash) && e.key.equals(key)) {
 9             return e.value;
10         }
11     }
12     return null;
13 }

View Code

3.3.6 put()

put() 的作用是对外提供接口,让 Hashtable 对象可以通过 put()将 “key-value” 添加到 Hashtable 中。

img

img

1 public synchronized V put(K key, V value) {
 2     // Hashtable中不能插入value为null的元素!!!
 3     if (value == null) {
 4         throw new NullPointerException();
 5     }
 7     // 若“Hashtable中已存在键为key的键值对”,
 8     // 则用“新的value”替换“旧的value”
 9     Entry tab[] = table;
10     int hash = key.hashCode();
11     int index = (hash & 0x7FFFFFFF) % tab.length;
12     for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
13         if ((e.hash == hash) && e.key.equals(key)) {
14             V old = e.value;
15             e.value = value;
16             return old;
17             }
18     }
20     // 若“Hashtable中不存在键为key的键值对”,
21     // (01) 将“修改统计数”+1
22     modCount++;
23     // (02) 若“Hashtable实际容量” > “阈值”(阈值=总的容量 * 加载因子)
24     //  则调整Hashtable的大小
25     if (count >= threshold) {
26         // Rehash the table if the threshold is exceeded
27         rehash();
29         tab = table;
30         index = (hash & 0x7FFFFFFF) % tab.length;
31     }
33     // (03) 将“Hashtable中index”位置的Entry(链表)保存到e中
34     Entry<K,V> e = tab[index];
35     // (04) 创建“新的Entry节点”,并将“新的Entry”插入“Hashtable的index位置”,并设置e为“新的Entry”的下一个元素(即“新Entry”为链表表头)。        
36     tab[index] = new Entry<K,V>(hash, key, value, e);
37     // (05) 将“Hashtable的实际容量”+1
38     count++;
39     return null;
40 }

View Code

3.3.7 putAll()

putAll() 的作用是将 “Map(t)” 的中全部元素逐一添加到 Hashtable 中

img

img

1 public synchronized void putAll(Map<? extends K, ? extends V> t) {
2     for (Map.Entry<? extends K, ? extends V> e : t.entrySet())
3         put(e.getKey(), e.getValue());
4 }

View Code

3.3.8 remove()

remove() 的作用就是删除 Hashtable 中键为 key 的元素

img

img

1 public synchronized V remove(Object key) {
 2     Entry tab[] = table;
 3     int hash = key.hashCode();
 4     int index = (hash & 0x7FFFFFFF) % tab.length;
 5     // 找到“key对应的Entry(链表)”
 6     // 然后在链表中找出要删除的节点,并删除该节点。
 7     for (Entry<K,V> e = tab[index], prev = null ; e != null ; prev = e, e = e.next) {
 8         if ((e.hash == hash) && e.key.equals(key)) {
 9             modCount++;
10             if (prev != null) {
11                 prev.next = e.next;
12             } else {
13                 tab[index] = e.next;
14             }
15             count--;
16             V oldValue = e.value;
17             e.value = null;
18             return oldValue;
19         }
20     }
21     return null;
22 }

View Code

第 3.4 部分 Hashtable 实现的 Cloneable 接口

Hashtable 实现了 Cloneable 接口,即实现了 clone() 方法。
clone() 方法的作用很简单,就是克隆一个 Hashtable 对象并返回。

img

img

1 // 克隆一个Hashtable,并以Object的形式返回。
 2 public synchronized Object clone() {
 3     try {
 4         Hashtable<K,V> t = (Hashtable<K,V>) super.clone();
 5         t.table = new Entry[table.length];
 6         for (int i = table.length ; i-- > 0 ; ) {
 7             t.table[i] = (table[i] != null)
 8             ? (Entry<K,V>) table[i].clone() : null;
 9         }
10         t.keySet = null;
11         t.entrySet = null;
12         t.values = null;
13         t.modCount = 0;
14         return t;
15     } catch (CloneNotSupportedException e) {
16         // this shouldn't happen, since we are Cloneable
17         throw new InternalError();
18     }
19 }

View Code

第 3.5 部分 Hashtable 实现的 Serializable 接口

Hashtable 实现 java.io.Serializable,分别实现了串行读取、写入功能。

串行写入函数就是将 Hashtable 的 “总的容量,实际容量,所有的 Entry” 都写入到输出流中
串行读取函数:根据写入方式读出将 Hashtable 的 “总的容量,实际容量,所有的 Entry” 依次读出

img

img

1 private synchronized void writeObject(java.io.ObjectOutputStream s)
 2     throws IOException
 3 {
 4     // Write out the length, threshold, loadfactor
 5     s.defaultWriteObject();
 7     // Write out length, count of elements and then the key/value objects
 8     s.writeInt(table.length);
 9     s.writeInt(count);
10     for (int index = table.length-1; index >= 0; index--) {
11         Entry entry = table[index];
13         while (entry != null) {
14         s.writeObject(entry.key);
15         s.writeObject(entry.value);
16         entry = entry.next;
17         }
18     }
19 }
21 private void readObject(java.io.ObjectInputStream s)
22      throws IOException, ClassNotFoundException
23 {
24     // Read in the length, threshold, and loadfactor
25     s.defaultReadObject();
27     // Read the original length of the array and number of elements
28     int origlength = s.readInt();
29     int elements = s.readInt();
31     // Compute new size with a bit of room 5% to grow but
32     // no larger than the original size.  Make the length
33     // odd if it's large enough, this helps distribute the entries.
34     // Guard against the length ending up zero, that's not valid.
35     int length = (int)(elements * loadFactor) + (elements / 20) + 3;
36     if (length > elements && (length & 1) == 0)
37         length--;
38     if (origlength > 0 && length > origlength)
39         length = origlength;
41     Entry[] table = new Entry[length];
42     count = 0;
44     // Read the number of elements and then all the key/value objects
45     for (; elements > 0; elements--) {
46         K key = (K)s.readObject();
47         V value = (V)s.readObject();
48             // synch could be eliminated for performance
49             reconstitutionPut(table, key, value);
50     }
51     this.table = table;
52 }

View Code

第 4 部分 Hashtable 遍历方式

4.1 遍历 Hashtable 的键值对

第一步:根据 entrySet()获取 Hashtable 的 “键值对” 的 Set 集合。
第二步:通过 Iterator 迭代器遍历 “第一步” 得到的集合。

[img](javascript:void(0)😉

// 假设table是Hashtable对象
// table中的key是String类型,value是Integer类型
Integer integ = null;
Iterator iter = table.entrySet().iterator();
while(iter.hasNext()) {
    Map.Entry entry = (Map.Entry)iter.next();
    // 获取key
    key = (String)entry.getKey();
        // 获取value
    integ = (Integer)entry.getValue();
}

[img](javascript:void(0)😉

4.2 通过 Iterator 遍历 Hashtable 的键

第一步:根据 keySet()获取 Hashtable 的 “键” 的 Set 集合。
第二步:通过 Iterator 迭代器遍历 “第一步” 得到的集合。

[img](javascript:void(0)😉

// 假设table是Hashtable对象
// table中的key是String类型,value是Integer类型
String key = null;
Integer integ = null;
Iterator iter = table.keySet().iterator();
while (iter.hasNext()) {
        // 获取key
    key = (String)iter.next();
        // 根据key,获取value
    integ = (Integer)table.get(key);
}

[img](javascript:void(0)😉

4.3 通过 Iterator 遍历 Hashtable 的值

第一步:根据 value()获取 Hashtable 的 “值” 的集合。
第二步:通过 Iterator 迭代器遍历 “第一步” 得到的集合。

[img](javascript:void(0)😉

// 假设table是Hashtable对象
// table中的key是String类型,value是Integer类型
Integer value = null;
Collection c = table.values();
Iterator iter= c.iterator();
while (iter.hasNext()) {
    value = (Integer)iter.next();
}

[img](javascript:void(0)😉

4.4 通过 Enumeration 遍历 Hashtable 的键

第一步:根据 keys() 获取 Hashtable 的集合。
第二步:通过 Enumeration 遍历 “第一步” 得到的集合。

Enumeration enu = table.keys();
while(enu.hasMoreElements()) {
    System.out.println(enu.nextElement());
}

4.5 通过 Enumeration 遍历 Hashtable 的值

第一步:根据 elements() 获取 Hashtable 的集合。
第二步:通过 Enumeration 遍历 “第一步” 得到的集合。

Enumeration enu = table.elements();
while(enu.hasMoreElements()) {
    System.out.println(enu.nextElement());
}

遍历测试程序如下

img

img

1 import java.util.*;
  3 /*
  4  * @desc 遍历Hashtable的测试程序。
  5  *   (01) 通过entrySet()去遍历key、value,参考实现函数:
  6  *        iteratorHashtableByEntryset()
  7  *   (02) 通过keySet()去遍历key,参考实现函数:
  8  *        iteratorHashtableByKeyset()
  9  *   (03) 通过values()去遍历value,参考实现函数:
 10  *        iteratorHashtableJustValues()
 11  *   (04) 通过Enumeration去遍历key,参考实现函数:
 12  *        enumHashtableKey()
 13  *   (05) 通过Enumeration去遍历value,参考实现函数:
 14  *        enumHashtableValue()
 15  *
 16  * @author skywang
 17  */
 18 public class HashtableIteratorTest {
 20     public static void main(String[] args) {
 21         int val = 0;
 22         String key = null;
 23         Integer value = null;
 24         Random r = new Random();
 25         Hashtable table = new Hashtable();
 27         for (int i=0; i<12; i++) {
 28             // 随机获取一个[0,100)之间的数字
 29             val = r.nextInt(100);
 31             key = String.valueOf(val);
 32             value = r.nextInt(5);
 33             // 添加到Hashtable中
 34             table.put(key, value);
 35             System.out.println(" key:"+key+" value:"+value);
 36         }
 37         // 通过entrySet()遍历Hashtable的key-value
 38         iteratorHashtableByEntryset(table) ;
 40         // 通过keySet()遍历Hashtable的key-value
 41         iteratorHashtableByKeyset(table) ;
 43         // 单单遍历Hashtable的value
 44         iteratorHashtableJustValues(table);        
 46         // 遍历Hashtable的Enumeration的key
 47         enumHashtableKey(table);
 49         // 遍历Hashtable的Enumeration的value
 50         //enumHashtableValue(table);
 51     }
 53     /*
 54      * 通过Enumeration遍历Hashtable的key
 55      * 效率高!
 56      */
 57     private static void enumHashtableKey(Hashtable table) {
 58         if (table == null)
 59             return ;
 61         System.out.println("\nenumeration Hashtable");
 62         Enumeration enu = table.keys();
 63         while(enu.hasMoreElements()) {
 64             System.out.println(enu.nextElement());
 65         }
 66     }
 69     /*
 70      * 通过Enumeration遍历Hashtable的value
 71      * 效率高!
 72      */
 73     private static void enumHashtableValue(Hashtable table) {
 74         if (table == null)
 75             return ;
 77         System.out.println("\nenumeration Hashtable");
 78         Enumeration enu = table.elements();
 79         while(enu.hasMoreElements()) {
 80             System.out.println(enu.nextElement());
 81         }
 82     }
 84     /*
 85      * 通过entry set遍历Hashtable
 86      * 效率高!
 87      */
 88     private static void iteratorHashtableByEntryset(Hashtable table) {
 89         if (table == null)
 90             return ;
 92         System.out.println("\niterator Hashtable By entryset");
 93         String key = null;
 94         Integer integ = null;
 95         Iterator iter = table.entrySet().iterator();
 96         while(iter.hasNext()) {
 97             Map.Entry entry = (Map.Entry)iter.next();
 99             key = (String)entry.getKey();
100             integ = (Integer)entry.getValue();
101             System.out.println(key+" -- "+integ.intValue());
102         }
103     }
105     /*
106      * 通过keyset来遍历Hashtable
107      * 效率低!
108      */
109     private static void iteratorHashtableByKeyset(Hashtable table) {
110         if (table == null)
111             return ;
113         System.out.println("\niterator Hashtable By keyset");
114         String key = null;
115         Integer integ = null;
116         Iterator iter = table.keySet().iterator();
117         while (iter.hasNext()) {
118             key = (String)iter.next();
119             integ = (Integer)table.get(key);
120             System.out.println(key+" -- "+integ.intValue());
121         }
122     }
125     /*
126      * 遍历Hashtable的values
127      */
128     private static void iteratorHashtableJustValues(Hashtable table) {
129         if (table == null)
130             return ;
132         Collection c = table.values();
133         Iterator iter= c.iterator();
134         while (iter.hasNext()) {
135             System.out.println(iter.next());
136        }
137     }
138 }

View Code

第 5 部分 Hashtable 示例

下面通过一个实例来学习如何使用 Hashtable。

img

img

1 import java.util.*;
 3 /*
 4  * @desc Hashtable的测试程序。
 5  *
 6  * @author skywang
 7  */
 8 public class HashtableTest {
 9     public static void main(String[] args) {
10         testHashtableAPIs();
11     }
13     private static void testHashtableAPIs() {
14         // 初始化随机种子
15         Random r = new Random();
16         // 新建Hashtable
17         Hashtable table = new Hashtable();
18         // 添加操作
19         table.put("one", r.nextInt(10));
20         table.put("two", r.nextInt(10));
21         table.put("three", r.nextInt(10));
23         // 打印出table
24         System.out.println("table:"+table );
26         // 通过Iterator遍历key-value
27         Iterator iter = table.entrySet().iterator();
28         while(iter.hasNext()) {
29             Map.Entry entry = (Map.Entry)iter.next();
30             System.out.println("next : "+ entry.getKey() +" - "+entry.getValue());
31         }
33         // Hashtable的键值对个数        
34         System.out.println("size:"+table.size());
36         // containsKey(Object key) :是否包含键key
37         System.out.println("contains key two : "+table.containsKey("two"));
38         System.out.println("contains key five : "+table.containsKey("five"));
40         // containsValue(Object value) :是否包含值value
41         System.out.println("contains value 0 : "+table.containsValue(new Integer(0)));
43         // remove(Object key) : 删除键key对应的键值对
44         table.remove("three");
46         System.out.println("table:"+table );
48         // clear() : 清空Hashtable
49         table.clear();
51         // isEmpty() : Hashtable是否为空
52         System.out.println((table.isEmpty()?"table is empty":"table is not empty") );
53     }
55 }

View Code

(某一次) 运行结果

[img](javascript:void(0)😉

table:{two=5, one=0, three=6}
next : two - 5
next : one - 0
next : three - 6
size:3
contains key two : true
contains key five : false
contains value 0 : true
table:{two=5, one=0}
table is empty

HashMap和HashTable的区别

1、继承的父类不同

Hashtable 继承自 Dictionary 类,而 HashMap 继承自 AbstractMap 类。但二者都实现了 Map 接口。

2、线程安全性不同

javadoc 中关于 hashmap 的一段描述如下:此实现不是同步的。如果多个线程同时访问一个哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须保持外部同步。

Hashtable 中的方法是 Synchronize 的,而 HashMap 中的方法在缺省情况下是非 Synchronize 的。在多线程并发的环境下,可以直接使用 Hashtable,不需要自己为它的方法实现同步,但使用 HashMap 时就必须要自己增加同步处理。(结构上的修改是指添加或删除一个或多个映射关系的任何操作;仅改变与实例已经包含的键关联的值不是结构上的修改。)这一般通过对自然封装该映射的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedMap 方法来 “包装” 该映射。最好在创建时完成这一操作,以防止对映射进行意外的非同步访问,如下所示:

Map m = Collections.synchronizedMap(new HashMap(...));

Hashtable 线程安全很好理解,因为它每个方法中都加入了 Synchronize。这里我们分析一下 HashMap 为什么是线程不安全的:

HashMap 底层是一个 Entry 数组,当发生 hash 冲突的时候,hashmap 是采用链表的方式来解决的,在对应的数组位置存放链表的头结点。对链表而言,新加入的节点会从头结点加入。

我们来分析一下多线程访问:

(1)在 hashmap 做 put 操作的时候会调用下面方法:

1. // 新增 Entry。将 “key-value” 插入指定位置,bucketIndex 是位置索引。   
2.   void addEntry(int hash, K key, V value, int bucketIndex) {   
3. ​    // 保存 “bucketIndex” 位置的值到 “e” 中   
4. ​    Entry<K,V> e = table[bucketIndex];   
5. ​    // 设置 “bucketIndex” 位置的元素为“新 Entry”,   
6. ​    // 设置 “e” 为“新 Entry 的下一个节点”   
7. ​    table[bucketIndex] = new Entry<K,V>(hash, key, value, e);   
8. ​    // 若 HashMap 的实际大小 不小于 “阈值”,则调整 HashMap 的大小   
9. ​    if (size++>= threshold)   
10. ​      resize(2 * table.length);   
11.   } 

在 hashmap 做 put 操作的时候会调用到以上的方法。现在假如 A 线程和 B 线程同时对同一个数组位置调用 addEntry,两个线程会同时得到现在的头结点,然后 A 写入新的头结点之后,B 也写入新的头结点,那 B 的写入操作就会覆盖 A 的写入操作造成 A 的写入操作丢失

( 2)删除键值对的代码

1. // 删除 “键为 key” 的元素   
2.   final Entry<K,V> removeEntryForKey(Object key) {   
3. ​    // 获取哈希值。若 key 为 null,则哈希值为 0;否则调用 hash() 进行计算   
4. ​    int hash = (key == null) ? 0 : hash(key.hashCode());   
5. ​    int i = indexFor(hash, table.length);   
6. ​    Entry<K,V> prev = table[i];   
7. ​    Entry<K,V> e = prev;   
8.    
9. ​    // 删除链表中 “键为 key” 的元素   
10. ​    // 本质是 “删除单向链表中的节点”   
11. ​    while (e != null) {   
12. ​      Entry<K,V> next = e.next;   
13. ​      Object k;   
14. ​      if (e.hash == hash &&   
15. ​        ((k = e.key) == key || (key != null && key.equals(k)))) {   
16. ​        modCount++;   
17. ​        size--;   
18. ​        if (prev == e)   
19. ​          table[i] = next;   
20. ​        else   
21. ​          prev.next = next;   
22. ​        e.recordRemoval(this);   
23. ​        return e;   
24. ​      }   
25. ​      prev = e;   
26. ​      e = next;   
27. ​    }   
28.    
29. ​    return e;   
30.   } 

当多个线程同时操作同一个数组位置的时候,也都会先取得现在状态下该位置存储的头结点,然后各自去进行计算操作,之后再把结果写会到该数组位置去,其实写回的时候可能其他的线程已经就把这个位置给修改过了,就会覆盖其他线程的修改

(3)addEntry 中当加入新的键值对后键值对总数量超过门限值的时候会调用一个 resize 操作,代码如下:

1. // 重新调整 HashMap 的大小,newCapacity 是调整后的容量   
2.   void resize(int newCapacity) {   
3. ​    Entry[] oldTable = table;   
4. ​    int oldCapacity = oldTable.length;   
5. ​    // 如果就容量已经达到了最大值,则不能再扩容,直接返回  
6. ​    if (oldCapacity == MAXIMUM_CAPACITY) {   
7. ​      threshold = Integer.MAX_VALUE;   
8. ​      return;   
9. ​    }   
10.    
11. ​    // 新建一个 HashMap,将 “旧 HashMap” 的全部元素添加到 “新 HashMap” 中,   
12. ​    // 然后,将 “新 HashMap” 赋值给“旧 HashMap”。   
13. ​    Entry[] newTable = new Entry[newCapacity];   
14. ​    transfer(newTable);   
15. ​    table = newTable;   
16. ​    threshold = (int)(newCapacity * loadFactor);   
17.   } 

这个操作会新生成一个新的容量的数组,然后对原数组的所有键值对重新进行计算和写入新的数组,之后指向新生成的数组。

当多个线程同时检测到总数量超过门限值的时候就会同时调用 resize 操作,各自生成新的数组并 rehash 后赋给该 map 底层的数组 table,结果最终只有最后一个线程生成的新数组被赋给 table 变量,其他线程的均会丢失。而且当某些线程已经完成赋值而其他线程刚开始的时候,就会用已经被赋值的 table 作为原始数组,这样也会有问题。

3、是否提供 contains 方法

HashMap 把 Hashtable 的 contains 方法去掉了,改成 containsValue 和 containsKey,因为 contains 方法容易让人引起误解。

Hashtable 则保留了 contains,containsValue 和 containsKey 三个方法,其中 contains 和 containsValue 功能相同。

我们看一下 Hashtable 的 ContainsKey 方法和 ContainsValue 的源码:

1. public boolean containsValue(Object value) {   
2.    return contains(value);   
3.  } 
1. // 判断 Hashtable 是否包含 “值 (value)”   
2.  public synchronized boolean contains(Object value) {   
3.    // 注意,Hashtable 中的 value 不能是 null,   
4.    // 若是 null 的话,抛出异常!   
5.    if (value == null) {   
6. ​     throw new NullPointerException();   
7.    }   
8.   
9.    // 从后向前遍历 table 数组中的元素 (Entry)   
10.    // 对于每个 Entry(单向链表),逐个遍历,判断节点的值是否等于 value   
11.    Entry tab[] = table;   
12.    for (int i = tab.length ; i-- > 0 ;) {   
13. ​     for (Entry<K,V> e = tab[i] ; e != null ; e = e.next) {   
14. ​       if (e.value.equals(value)) {   
15. ​         return true;   
16. ​       }   
17. ​     }   
18.    }   
19.    return false;   
20.  } 
1. // 判断 Hashtable 是否包含 key   
2.  public synchronized boolean containsKey(Object key) {   
3.    Entry tab[] = table;   
4. / 计算 hash 值,直接用 key 的 hashCode 代替  
5.    int hash = key.hashCode();    
6.    // 计算在数组中的索引值   
7.    int index = (hash & 0x7FFFFFFF) % tab.length;   
8.    // 找到 “key 对应的 Entry(链表)”,然后在链表中找出“哈希值” 和“键值”与 key 都相等的元素   
9.    for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {   
10. ​     if ((e.hash == hash) && e.key.equals(key)) {   
11. ​       return true;   
12. ​     }   
13.    }   
14.    return false;   
15.  } 

下面我们看一下 HashMap 的 ContainsKey 方法和 ContainsValue 的源码:

1. // HashMap 是否包含 key   
2.   public boolean containsKey(Object key) {   
3. ​    return getEntry(key) != null;   
4.   }
1. // 返回 “键为 key” 的键值对   
2.   final Entry<K,V> getEntry(Object key) {   
3. ​    // 获取哈希值   
4. ​    // HashMap 将 “key 为 null” 的元素存储在 table[0]位置,“key 不为 null”的则调用 hash()计算哈希值   
5. ​    int hash = (key == null) ? 0 : hash(key.hashCode());   
6. ​    // 在 “该 hash 值对应的链表” 上查找 “键值等于 key” 的元素   
7. ​    for (Entry<K,V> e = table[indexFor(hash, table.length)];   
8. ​       e != null;   
9. ​       e = e.next) {   
10. ​      Object k;   
11. ​      if (e.hash == hash &&   
12. ​        ((k = e.key) == key || (key != null && key.equals(k))))   
13. ​        return e;   
14. ​    }   
15. ​    return null;   
16.   } 
1. // 是否包含 “值为 value” 的元素   
2.   public boolean containsValue(Object value) {   
3.   // 若 “value 为 null”,则调用 containsNullValue() 查找   
4.   if (value == null)   
5. ​      return containsNullValue();   
6.    
7.   // 若 “value 不为 null”,则查找 HashMap 中是否有值为 value 的节点。   
8.   Entry[] tab = table;   
9. ​    for (int i = 0; i <tab.length ; i++)   
10. ​      for (Entry e = tab[i] ; e != null ; e = e.next)   
11. ​        if (value.equals(e.value))   
12. ​          return true;   
13.   return false;   
14.   } 

通过上面源码的比较,我们可以得到第四个不同的地方

4、key 和 value 是否允许 null 值

其中 key 和 value 都是对象,并且不能包含重复 key,但可以包含重复的 value。

通过上面的 ContainsKey 方法和 ContainsValue 的源码我们可以很明显的看出:

Hashtable 中,key 和 value 都不允许出现 null 值。但是如果在 Hashtable 中有类似 put(null,null) 的操作,编译同样可以通过,因为 key 和 value 都是 Object 类型,但运行时会抛出 NullPointerException 异常,这是 JDK 的规范规定的。
HashMap 中,null 可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为 null。当 get() 方法返回 null 值时,可能是 HashMap 中没有该键,也可能使该键所对应的值为 null。因此,在 HashMap 中不能由 get() 方法来判断 HashMap 中是否存在某个键, 而应该用 containsKey() 方法来判断。

5、两个遍历方式的内部实现上不同

Hashtable、HashMap 都使用了 Iterator。而由于历史原因,Hashtable 还使用了 Enumeration 的方式 。

6、hash 值不同

哈希值的使用不同,HashTable 直接使用对象的 hashCode。而 HashMap 重新计算 hash 值

hashCode 是 jdk 根据对象的地址或者字符串或者数字算出来的 int 类型的数值。

Hashtable 计算 hash 值,直接用 key 的 hashCode(),而 HashMap 重新计算了 key 的 hash 值,Hashtable 在求 hash 值对应的位置索引时,用取模运算,而 HashMap 在求位置索引时,则用与运算,且这里一般先用 hash&0x7FFFFFFF 后,再对 length 取模,&0x7FFFFFFF 的目的是为了将负的 hash 值转化为正值,因为 hash 值有可能为负数,而 & 0x7FFFFFFF 后,只有符号外改变,而后面的位都不变。

7、内部实现使用的数组初始化和扩容方式不同

HashTable 在不指定容量的情况下的默认容量为 11,而 HashMap 为 16,Hashtable 不要求底层数组的容量一定要为 2 的整数次幂,而 HashMap 则要求一定为 2 的整数次幂。
Hashtable 扩容时,将容量变为原来的 2 倍加 1,而 HashMap 扩容时,将容量变为原来的 2 倍。

Hashtable 和 HashMap 它们两个内部实现方式的数组的初始大小和扩容的方式。HashTable 中 hash 数组默认大小是 11,增加的方式是 old*2+1。

posted @ 2020-03-15 10:18  别再闹了  阅读(439)  评论(0)    收藏  举报