概要
前一章,我们学习了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的构造函数
// 默认构造函数。 public Hashtable() // 指定“容量大小”的构造函数 public Hashtable(int initialCapacity) // 指定“容量大小”和“加载因子”的构造函数 public Hashtable(int initialCapacity, float loadFactor) // 包含“子Map”的构造函数 public Hashtable(Map<? extends K, ? extends V> t)
Hashtable的API
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()
第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关系如下图:
从图中可以看出:
(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。

1 package java.util; 2 import java.io.*; 3 4 public class Hashtable<K,V> 5 extends Dictionary<K,V> 6 implements Map<K,V>, Cloneable, java.io.Serializable { 7 8 // Hashtable保存key-value的数组。 9 // Hashtable是采用拉链法实现的,每一个Entry本质上是一个单向链表 10 private transient Entry[] table; 11 12 // Hashtable中元素的实际数量 13 private transient int count; 14 15 // 阈值,用于判断是否需要调整Hashtable的容量(threshold = 容量*加载因子) 16 private int threshold; 17 18 // 加载因子 19 private float loadFactor; 20 21 // Hashtable被改变的次数 22 private transient int modCount = 0; 23 24 // 序列版本号 25 private static final long serialVersionUID = 1421746759512286392L; 26 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); 34 35 if (initialCapacity==0) 36 initialCapacity = 1; 37 this.loadFactor = loadFactor; 38 table = new Entry[initialCapacity]; 39 threshold = (int)(initialCapacity * loadFactor); 40 } 41 42 // 指定“容量大小”的构造函数 43 public Hashtable(int initialCapacity) { 44 this(initialCapacity, 0.75f); 45 } 46 47 // 默认构造函数。 48 public Hashtable() { 49 // 默认构造函数,指定的容量大小是11;加载因子是0.75 50 this(11, 0.75f); 51 } 52 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 } 59 60 public synchronized int size() { 61 return count; 62 } 63 64 public synchronized boolean isEmpty() { 65 return count == 0; 66 } 67 68 // 返回“所有key”的枚举对象 69 public synchronized Enumeration<K> keys() { 70 return this.<K>getEnumeration(KEYS); 71 } 72 73 // 返回“所有value”的枚举对象 74 public synchronized Enumeration<V> elements() { 75 return this.<V>getEnumeration(VALUES); 76 } 77 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 } 85 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 } 98 99 public boolean containsValue(Object value) { 100 return contains(value); 101 } 102 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 } 118 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 } 133 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; 141 142 int newCapacity = oldCapacity * 2 + 1; 143 Entry[] newMap = new Entry[newCapacity]; 144 145 modCount++; 146 threshold = (int)(newCapacity * loadFactor); 147 table = newMap; 148 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; 153 154 int index = (e.hash & 0x7FFFFFFF) % newCapacity; 155 e.next = newMap[index]; 156 newMap[index] = e; 157 } 158 } 159 } 160 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 } 167 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 } 180 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(); 189 190 tab = table; 191 index = (hash & 0x7FFFFFFF) % tab.length; 192 } 193 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 } 202 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 } 226 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 } 232 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 } 242 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 } 262 263 public synchronized String toString() { 264 int max = size() - 1; 265 if (max == -1) 266 return "{}"; 267 268 StringBuilder sb = new StringBuilder(); 269 Iterator<Map.Entry<K,V>> it = entrySet().iterator(); 270 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()); 279 280 if (i == max) 281 return sb.append('}').toString(); 282 sb.append(", "); 283 } 284 } 285 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 } 296 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 } 307 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; 314 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 } 322 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 } 342 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 } 350 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 } 357 358 public boolean add(Map.Entry<K,V> o) { 359 return super.add(o); 360 } 361 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; 373 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 } 379 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; 391 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; 400 401 count--; 402 e.value = null; 403 return true; 404 } 405 } 406 return false; 407 } 408 409 public int size() { 410 return count; 411 } 412 413 public void clear() { 414 Hashtable.this.clear(); 415 } 416 } 417 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 } 426 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 } 443 444 // 重新equals()函数 445 // 若两个Hashtable的所有key-value键值对都相等,则判断它们两个相等 446 public synchronized boolean equals(Object o) { 447 if (o == this) 448 return true; 449 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; 455 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 } 478 479 return true; 480 } 481 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 489 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 496 497 return h; 498 } 499 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(); 507 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]; 513 514 while (entry != null) { 515 s.writeObject(entry.key); 516 s.writeObject(entry.value); 517 entry = entry.next; 518 } 519 } 520 } 521 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(); 529 530 // Read the original length of the array and number of elements 531 int origlength = s.readInt(); 532 int elements = s.readInt(); 533 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; 543 544 Entry[] table = new Entry[length]; 545 count = 0; 546 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 } 556 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 } 577 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; 587 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 } 595 596 protected Object clone() { 597 return new Entry<K,V>(hash, key, value, 598 (next==null ? null : (Entry<K,V>) next.clone())); 599 } 600 601 public K getKey() { 602 return key; 603 } 604 605 public V getValue() { 606 return value; 607 } 608 609 // 设置value。若value是null,则抛出异常。 610 public V setValue(V value) { 611 if (value == null) 612 throw new NullPointerException(); 613 614 V oldValue = this.value; 615 this.value = value; 616 return oldValue; 617 } 618 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; 625 626 return (key==null ? e.getKey()==null : key.equals(e.getKey())) && 627 (value==null ? e.getValue()==null : value.equals(e.getValue())); 628 } 629 630 public int hashCode() { 631 return hash ^ (value==null ? 0 : value.hashCode()); 632 } 633 634 public String toString() { 635 return key.toString()+"="+value.toString(); 636 } 637 } 638 639 private static final int KEYS = 0; 640 private static final int VALUES = 1; 641 private static final int ENTRIES = 2; 642 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; 652 653 // Enumerator是 “迭代器(Iterator)” 还是 “枚举类(Enumeration)”的标志 654 // iterator为true,表示它是迭代器;否则,是枚举类。 655 boolean iterator; 656 657 // 在将Enumerator当作迭代器使用时会用到,用来实现fail-fast机制。 658 protected int expectedModCount = modCount; 659 660 Enumerator(int type, boolean iterator) { 661 this.type = type; 662 this.iterator = iterator; 663 } 664 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 } 678 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 } 700 701 // 迭代器Iterator的判断是否存在下一个元素 702 // 实际上,它是调用的hasMoreElements() 703 public boolean hasNext() { 704 return hasMoreElements(); 705 } 706 707 // 迭代器获取下一个元素 708 // 实际上,它是调用的nextElement() 709 public T next() { 710 if (modCount != expectedModCount) 711 throw new ConcurrentModificationException(); 712 return nextElement(); 713 } 714 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(); 725 726 synchronized(Hashtable.this) { 727 Entry[] tab = Hashtable.this.table; 728 int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length; 729 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 } 748 749 750 private static Enumeration emptyEnumerator = new EmptyEnumerator(); 751 private static Iterator emptyIterator = new EmptyIterator(); 752 753 // 空枚举类 754 // 当Hashtable的实际大小为0;此时,又要通过Enumeration遍历Hashtable时,返回的是“空枚举类”的对象。 755 private static class EmptyEnumerator implements Enumeration<Object> { 756 757 EmptyEnumerator() { 758 } 759 760 // 空枚举类的hasMoreElements() 始终返回false 761 public boolean hasMoreElements() { 762 return false; 763 } 764 765 // 空枚举类的nextElement() 抛出异常 766 public Object nextElement() { 767 throw new NoSuchElementException("Hashtable Enumerator"); 768 } 769 } 770 771 772 // 空迭代器 773 // 当Hashtable的实际大小为0;此时,又要通过迭代器遍历Hashtable时,返回的是“空迭代器”的对象。 774 private static class EmptyIterator implements Iterator<Object> { 775 776 EmptyIterator() { 777 } 778 779 public boolean hasNext() { 780 return false; 781 } 782 783 public Object next() { 784 throw new NoSuchElementException("Hashtable Iterator"); 785 } 786 787 public void remove() { 788 throw new IllegalStateException("Hashtable Iterator"); 789 } 790 791 } 792 }
说明: 在详细介绍Hashtable的代码之前,我们需要了解:和Hashmap一样,Hashtable也是一个散列表,它也是通过“拉链法”解决哈希冲突的。
第3.1部分 Hashtable的“拉链法”相关内容
3.1.1 Hashtable数据存储数组
private transient Entry[] table;
Hashtable中的key-value都是存储在table数组中的。
3.1.2 数据节点Entry的数据结构

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; 8 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 } 16 17 protected Object clone() { 18 return new Entry<K,V>(hash, key, value, 19 (next==null ? null : (Entry<K,V>) next.clone())); 20 } 21 22 public K getKey() { 23 return key; 24 } 25 26 public V getValue() { 27 return value; 28 } 29 30 // 设置value。若value是null,则抛出异常。 31 public V setValue(V value) { 32 if (value == null) 33 throw new NullPointerException(); 34 35 V oldValue = this.value; 36 this.value = value; 37 return oldValue; 38 } 39 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; 46 47 return (key==null ? e.getKey()==null : key.equals(e.getKey())) && 48 (value==null ? e.getValue()==null : value.equals(e.getValue())); 49 } 50 51 public int hashCode() { 52 return hash ^ (value==null ? 0 : value.hashCode()); 53 } 54 55 public String toString() { 56 return key.toString()+"="+value.toString(); 57 } 58 }
从中,我们可以看出 Entry 实际上就是一个单向链表。这也是为什么我们说Hashtable是通过拉链法解决哈希冲突的。
Entry 实现了Map.Entry 接口,即实现getKey(), getValue(), setValue(V value), equals(Object o), hashCode()这些函数。这些都是基本的读取/修改key、value值的函数。
第3.2部分 Hashtable的构造函数
Hashtable共包括4个构造函数

1 // 默认构造函数。 2 public Hashtable() { 3 // 默认构造函数,指定的容量大小是11;加载因子是0.75 4 this(11, 0.75f); 5 } 6 7 // 指定“容量大小”的构造函数 8 public Hashtable(int initialCapacity) { 9 this(initialCapacity, 0.75f); 10 } 11 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); 19 20 if (initialCapacity==0) 21 initialCapacity = 1; 22 this.loadFactor = loadFactor; 23 table = new Entry[initialCapacity]; 24 threshold = (int)(initialCapacity * loadFactor); 25 } 26 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 }
第3.3部分 Hashtable的主要对外接口
3.3.1 clear()
clear() 的作用是清空Hashtable。它是将Hashtable的table数组的值全部设为null

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 }
3.3.2 contains() 和 containsValue()
contains() 和 containsValue() 的作用都是判断Hashtable是否包含“值(value)”

1 public boolean containsValue(Object value) { 2 return contains(value); 3 } 4 5 public synchronized boolean contains(Object value) { 6 // Hashtable中“键值对”的value不能是null, 7 // 若是null的话,抛出异常! 8 if (value == null) { 9 throw new NullPointerException(); 10 } 11 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 }
3.3.3 containsKey()
containsKey() 的作用是判断Hashtable是否包含key

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 }
3.3.4 elements()
elements() 的作用是返回“所有value”的枚举对象

1 public synchronized Enumeration<V> elements() { 2 return this.<V>getEnumeration(VALUES); 3 } 4 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 }
从中,我们可以看出:
(01) 若Hashtable的实际大小为0,则返回“空枚举类”对象emptyEnumerator;
(02) 否则,返回正常的Enumerator的对象。(Enumerator实现了迭代器和枚举两个接口)
我们先看看emptyEnumerator对象是如何实现的

1 private static Enumeration emptyEnumerator = new EmptyEnumerator(); 2 3 // 空枚举类 4 // 当Hashtable的实际大小为0;此时,又要通过Enumeration遍历Hashtable时,返回的是“空枚举类”的对象。 5 private static class EmptyEnumerator implements Enumeration<Object> { 6 7 EmptyEnumerator() { 8 } 9 10 // 空枚举类的hasMoreElements() 始终返回false 11 public boolean hasMoreElements() { 12 return false; 13 } 14 15 // 空枚举类的nextElement() 抛出异常 16 public Object nextElement() { 17 throw new NoSuchElementException("Hashtable Enumerator"); 18 } 19 }
我们在来看看Enumeration类
Enumerator的作用是提供了“通过elements()遍历Hashtable的接口” 和 “通过entrySet()遍历Hashtable的接口”。因为,它同时实现了 “Enumerator接口”和“Iterator接口”。

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; 9 10 // Enumerator是 “迭代器(Iterator)” 还是 “枚举类(Enumeration)”的标志 11 // iterator为true,表示它是迭代器;否则,是枚举类。 12 boolean iterator; 13 14 // 在将Enumerator当作迭代器使用时会用到,用来实现fail-fast机制。 15 protected int expectedModCount = modCount; 16 17 Enumerator(int type, boolean iterator) { 18 this.type = type; 19 this.iterator = iterator; 20 } 21 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 } 35 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 } 57 58 // 迭代器Iterator的判断是否存在下一个元素 59 // 实际上,它是调用的hasMoreElements() 60 public boolean hasNext() { 61 return hasMoreElements(); 62 } 63 64 // 迭代器获取下一个元素 65 // 实际上,它是调用的nextElement() 66 public T next() { 67 if (modCount != expectedModCount) 68 throw new ConcurrentModificationException(); 69 return nextElement(); 70 } 71 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(); 82 83 synchronized(Hashtable.this) { 84 Entry[] tab = Hashtable.this.table; 85 int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length; 86 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 }
entrySet(), keySet(), keys(), values()的实现方法和elements()差不多,而且源码中已经明确的给出了注释。这里就不再做过多说明了。
3.3.5 get()
get() 的作用就是获取key对应的value,没有的话返回null

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 }
3.3.6 put()
put() 的作用是对外提供接口,让Hashtable对象可以通过put()将“key-value”添加到Hashtable中。

1 public synchronized V put(K key, V value) { 2 // Hashtable中不能插入value为null的元素!!! 3 if (value == null) { 4 throw new NullPointerException(); 5 } 6 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 } 19 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(); 28 29 tab = table; 30 index = (hash & 0x7FFFFFFF) % tab.length; 31 } 32 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 }
3.3.7 putAll()
putAll() 的作用是将“Map(t)”的中全部元素逐一添加到Hashtable中

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 }
3.3.8 remove()
remove() 的作用就是删除Hashtable中键为key的元素

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 }
第3.4部分 Hashtable实现的Cloneable接口
Hashtable实现了Cloneable接口,即实现了clone()方法。
clone()方法的作用很简单,就是克隆一个Hashtable对象并返回。

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 }
第3.5部分 Hashtable实现的Serializable接口
Hashtable实现java.io.Serializable,分别实现了串行读取、写入功能。
串行写入函数就是将Hashtable的“总的容量,实际容量,所有的Entry”都写入到输出流中
串行读取函数:根据写入方式读出将Hashtable的“总的容量,实际容量,所有的Entry”依次读出

1 private synchronized void writeObject(java.io.ObjectOutputStream s) 2 throws IOException 3 { 4 // Write out the length, threshold, loadfactor 5 s.defaultWriteObject(); 6 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]; 12 13 while (entry != null) { 14 s.writeObject(entry.key); 15 s.writeObject(entry.value); 16 entry = entry.next; 17 } 18 } 19 } 20 21 private void readObject(java.io.ObjectInputStream s) 22 throws IOException, ClassNotFoundException 23 { 24 // Read in the length, threshold, and loadfactor 25 s.defaultReadObject(); 26 27 // Read the original length of the array and number of elements 28 int origlength = s.readInt(); 29 int elements = s.readInt(); 30 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; 40 41 Entry[] table = new Entry[length]; 42 count = 0; 43 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 }
第4部分 Hashtable遍历方式
4.1 遍历Hashtable的键值对
第一步:根据entrySet()获取Hashtable的“键值对”的Set集合。
第二步:通过Iterator迭代器遍历“第一步”得到的集合。
// 假设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(); }
4.2 通过Iterator遍历Hashtable的键
第一步:根据keySet()获取Hashtable的“键”的Set集合。
第二步:通过Iterator迭代器遍历“第一步”得到的集合。
// 假设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); }
4.3 通过Iterator遍历Hashtable的值
第一步:根据value()获取Hashtable的“值”的集合。
第二步:通过Iterator迭代器遍历“第一步”得到的集合。
// 假设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(); }
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()); }
遍历测试程序如下:

1 import java.util.*; 2 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 { 19 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(); 26 27 for (int i=0; i<12; i++) { 28 // 随机获取一个[0,100)之间的数字 29 val = r.nextInt(100); 30 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) ; 39 40 // 通过keySet()遍历Hashtable的key-value 41 iteratorHashtableByKeyset(table) ; 42 43 // 单单遍历Hashtable的value 44 iteratorHashtableJustValues(table); 45 46 // 遍历Hashtable的Enumeration的key 47 enumHashtableKey(table); 48 49 // 遍历Hashtable的Enumeration的value 50 //enumHashtableValue(table); 51 } 52 53 /* 54 * 通过Enumeration遍历Hashtable的key 55 * 效率高! 56 */ 57 private static void enumHashtableKey(Hashtable table) { 58 if (table == null) 59 return ; 60 61 System.out.println("\nenumeration Hashtable"); 62 Enumeration enu = table.keys(); 63 while(enu.hasMoreElements()) { 64 System.out.println(enu.nextElement()); 65 } 66 } 67 68 69 /* 70 * 通过Enumeration遍历Hashtable的value 71 * 效率高! 72 */ 73 private static void enumHashtableValue(Hashtable table) { 74 if (table == null) 75 return ; 76 77 System.out.println("\nenumeration Hashtable"); 78 Enumeration enu = table.elements(); 79 while(enu.hasMoreElements()) { 80 System.out.println(enu.nextElement()); 81 } 82 } 83 84 /* 85 * 通过entry set遍历Hashtable 86 * 效率高! 87 */ 88 private static void iteratorHashtableByEntryset(Hashtable table) { 89 if (table == null) 90 return ; 91 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(); 98 99 key = (String)entry.getKey(); 100 integ = (Integer)entry.getValue(); 101 System.out.println(key+" -- "+integ.intValue()); 102 } 103 } 104 105 /* 106 * 通过keyset来遍历Hashtable 107 * 效率低! 108 */ 109 private static void iteratorHashtableByKeyset(Hashtable table) { 110 if (table == null) 111 return ; 112 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 } 123 124 125 /* 126 * 遍历Hashtable的values 127 */ 128 private static void iteratorHashtableJustValues(Hashtable table) { 129 if (table == null) 130 return ; 131 132 Collection c = table.values(); 133 Iterator iter= c.iterator(); 134 while (iter.hasNext()) { 135 System.out.println(iter.next()); 136 } 137 } 138 }
第5部分 Hashtable示例
下面通过一个实例来学习如何使用Hashtable。

1 import java.util.*; 2 3 /* 4 * @desc Hashtable的测试程序。 5 * 6 * @author skywang 7 */ 8 public class HashtableTest { 9 public static void main(String[] args) { 10 testHashtableAPIs(); 11 } 12 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)); 22 23 // 打印出table 24 System.out.println("table:"+table ); 25 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 } 32 33 // Hashtable的键值对个数 34 System.out.println("size:"+table.size()); 35 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")); 39 40 // containsValue(Object value) :是否包含值value 41 System.out.println("contains value 0 : "+table.containsValue(new Integer(0))); 42 43 // remove(Object key) : 删除键key对应的键值对 44 table.remove("three"); 45 46 System.out.println("table:"+table ); 47 48 // clear() : 清空Hashtable 49 table.clear(); 50 51 // isEmpty() : Hashtable是否为空 52 System.out.println((table.isEmpty()?"table is empty":"table is not empty") ); 53 } 54 55 }
(某一次)运行结果:
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
更多内容
Java 集合系列10之 HashMap详细介绍(源码解析)和使用示例
Java 集合系列11之 Hashtable详细介绍(源码解析)和使用示例
Java 集合系列12之 TreeMap详细介绍(源码解析)和使用示例
Java 集合系列13之 WeakHashMap详细介绍(源码解析)和使用示例
Java 集合系列14之 Map总结(HashMap, Hashtable, TreeMap, WeakHashMap等使用场景)
Java 集合系列18之 Iterator和Enumeration比较
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix