JDK1.8 Hashtable详解

向右看齐 2023-06-20 06:21 75阅读 0赞

Hashtable 是一个散列表,它存储的内容是键值对(key-value)映射。

1 Hashtable类定义

  1. public class Hashtable<K,V>
  2. extends Dictionary<K,V>
  3. implements Map<K,V>, Cloneable, java.io.Serializable {
  4. }
  5. //内部静态类Entry,Entry为存储的节点
  6. //Entry本质为链表
  7. private static class Entry<K,V> implements Map.Entry<K,V> {
  8. //哈希值
  9. final int hash;
  10. //键值
  11. final K key;
  12. //存储的数据值
  13. V value;
  14. //指向下一个节点
  15. Entry<K,V> next;
  16. protected Entry(int hash, K key, V value, Entry<K,V> next) {
  17. this.hash = hash;
  18. this.key = key;
  19. this.value = value;
  20. this.next = next;
  21. }
  22. @SuppressWarnings("unchecked")
  23. protected Object clone() {
  24. return new Entry<>(hash, key, value,
  25. (next==null ? null : (Entry<K,V>) next.clone()));
  26. }
  27. // Map.Entry Ops
  28. public K getKey() {
  29. return key;
  30. }
  31. public V getValue() {
  32. return value;
  33. }
  34. public V setValue(V value) {
  35. if (value == null)
  36. throw new NullPointerException();
  37. V oldValue = this.value;
  38. this.value = value;
  39. return oldValue;
  40. }
  41. public boolean equals(Object o) {
  42. if (!(o instanceof Map.Entry))
  43. return false;
  44. Map.Entry<?,?> e = (Map.Entry<?,?>)o;
  45. return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
  46. (value==null ? e.getValue()==null : value.equals(e.getValue()));
  47. }
  48. public int hashCode() {
  49. return hash ^ Objects.hashCode(value);
  50. }
  51. public String toString() {
  52. return key.toString()+"="+value.toString();
  53. }
  54. }

1)Hashtable继承于Dictionary抽象类,Dictionary中定义了对于容器操作的多种抽象方法。
2)实现Map接口,Hashtable实现了Map接口中定义的方法,具体的方法将在后文中分析。
3)实现Cloneable接口。
4)实现Serializable接口,可序列化。

2 属性值

Hashtable的属性值含义已在代码注释中说明

  1. // 保存key-value的数组,支持泛型
  2. // Entry同样采用链表解决冲突,每一个Entry本质上是一个单向链表
  3. private transient Entry<?,?>[] table;
  4. // Entry中键值对的数量
  5. private transient int count;
  6. // 阈值,用于判断是否需要调整Entry的容量
  7. private int threshold;
  8. // 负载因子,当元素个数count大于总容量 * 负载因子时,扩容
  9. private float loadFactor;
  10. // Entry被改变的次数,用于fail-fast机制的实现
  11. private transient int modCount = 0;

3 构造方法

1)无参构造方法

  1. //无参构造方法
  2. public Hashtable() {
  3. //默认容量大小为11,负载因子设置为0.75
  4. this(11, 0.75f);
  5. }

2)初始化容量大小为initialCapacity

  1. //带有初始化容量大小的构造方法
  2. public Hashtable(int initialCapacity) {
  3. this(initialCapacity, 0.75f);
  4. }

3) 初始化容量为initialCapacity,负载因子为 loadFactor

  1. public Hashtable(int initialCapacity, float loadFactor) {
  2. //检查参数的合法性
  3. if (initialCapacity < 0)
  4. throw new IllegalArgumentException("Illegal Capacity: "+
  5. initialCapacity);
  6. if (loadFactor <= 0 || Float.isNaN(loadFactor))
  7. throw new IllegalArgumentException("Illegal Load: "+loadFactor);
  8. //如果设置初始容量为0,则默认修改为1
  9. if (initialCapacity==0)
  10. initialCapacity = 1;
  11. //设置负载因子
  12. this.loadFactor = loadFactor;
  13. //根据设置的初始化容量创建数组
  14. table = new Entry<?,?>[initialCapacity];
  15. //计算阈值,取初始化容量与可分配的最大容量中的最小值。
  16. threshold = (int)Math.min(initialCapacity, MAX_ARRAY_SIZE + 1);
  17. }

4) 使用Map集合初始化

  1. //使用Map集合初始化
  2. public Hashtable(Map<? extends K, ? extends V> t) {
  3. // 若集合t元素大于5,则初始化容量为集合t中元素数目的2倍
  4. // 否则初始化容量为11
  5. // 负载因子设置为0.75
  6. this(Math.max(2*t.size(), 11), 0.75f);
  7. //将集合t中元素全部存储
  8. putAll(t);
  9. }
  10. public synchronized void putAll(Map<? extends K, ? extends V> t) {
  11. // for循环遍历集合t,将t中元素存储到this集合中
  12. for (Map.Entry<? extends K, ? extends V> e : t.entrySet())
  13. //将键值对添加至集合中
  14. put(e.getKey(), e.getValue());
  15. }

4 核心方法
































































方法名 含义 时间复杂度
get(Object key) 根据key值获取元素 O(1)
put(K key, V value) 添加元素 O(n)
putAll() 添加集合中元素 O(n)
contains(Object value) 判断是否包含元素 O(n)
containsValue(Object value) 判断是否包含元素 O(n)
containsKey(Object key) 判断是否包含key O(1)
replace(K key, V oldValue, V newValue) 替换元素值 O(1)
size() 获取元素数目 O(1)
isEmpty() 判断集合是否为空 O(1)
remove(Object key) 根据键值删除元素 O(n)
clear() 清空集合 O(n)

5 get()方法

  1. public synchronized V get(Object key) {
  2. Entry<?,?> tab[] = table;
  3. //得到key的hashcode
  4. int hash = key.hashCode();
  5. //根据hashcode计算索引值
  6. int index = (hash & 0x7FFFFFFF) % tab.length;
  7. //根据index找到key对应Entry链表,遍历链表找到哈希值与键值均与key相同的元素
  8. for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
  9. if ((e.hash == hash) && e.key.equals(key)) {
  10. return (V)e.value;
  11. }
  12. }
  13. // 若没有找到,则返回null
  14. return null;
  15. }

6 put()方法

  1. public synchronized V put(K key, V value) {
  2. // 检验数据值的合法性
  3. if (value == null) {
  4. throw new NullPointerException();
  5. }
  6. Entry<?,?> tab[] = table;
  7. //根据键值获取索引index
  8. int hash = key.hashCode();
  9. int index = (hash & 0x7FFFFFFF) % tab.length;
  10. //采用for循环方式解决哈希冲突,如果出现冲突则放在链表末尾。
  11. @SuppressWarnings("unchecked")
  12. Entry<K,V> entry = (Entry<K,V>)tab[index];
  13. for(; entry != null ; entry = entry.next) {
  14. //当前键值key已存在,更新key的映射值value,并返回旧值
  15. if ((entry.hash == hash) && entry.key.equals(key)) {
  16. V old = entry.value;
  17. entry.value = value;
  18. return old;
  19. }
  20. }
  21. //若没有找到重复键值key,则将key和value添加链表末尾
  22. addEntry(hash, key, value, index);
  23. return null;
  24. }
  25. //添加元素
  26. private void addEntry(int hash, K key, V value, int index) {
  27. modCount++;
  28. Entry<?,?> tab[] = table;
  29. //判断当前数目是否超过阈值
  30. if (count >= threshold) {
  31. // 数目超过阈值,扩容
  32. rehash();
  33. //更新扩容后的数组信息
  34. tab = table;
  35. hash = key.hashCode();
  36. index = (hash & 0x7FFFFFFF) % tab.length;
  37. }
  38. // 没有超过阈值,则添加至数组中
  39. @SuppressWarnings("unchecked")
  40. Entry<K,V> e = (Entry<K,V>) tab[index];
  41. tab[index] = new Entry<>(hash, key, value, e);
  42. //增加元素数目
  43. count++;
  44. }
  45. //扩容方法
  46. protected void rehash() {
  47. //获取旧数组大小
  48. int oldCapacity = table.length;
  49. Entry<?,?>[] oldMap = table;
  50. // 创建新容量大小的Entry数组,数组容量大小为原数组的2倍+1
  51. int newCapacity = (oldCapacity << 1) + 1;
  52. if (newCapacity - MAX_ARRAY_SIZE > 0) {
  53. if (oldCapacity == MAX_ARRAY_SIZE)
  54. // Keep running with MAX_ARRAY_SIZE buckets
  55. return;
  56. newCapacity = MAX_ARRAY_SIZE;
  57. }
  58. Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
  59. modCount++;
  60. //重新计算阈值
  61. threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
  62. table = newMap;
  63. //将原数组中元素拷贝至新数组
  64. for (int i = oldCapacity ; i-- > 0 ;) {
  65. for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
  66. Entry<K,V> e = old;
  67. old = old.next;
  68. int index = (e.hash & 0x7FFFFFFF) % newCapacity;
  69. e.next = (Entry<K,V>)newMap[index];
  70. newMap[index] = e;
  71. }
  72. }
  73. }

7 contains()方法

  1. //判断是否含有value
  2. public boolean containsValue(Object value) {
  3. return contains(value);
  4. }
  5. public synchronized boolean contains(Object value) {
  6. //检查参数的合法性
  7. if (value == null) {
  8. throw new NullPointerException();
  9. }
  10. // 双重for循环,外循环遍历数组,内循环遍历链表
  11. Entry<?,?> tab[] = table;
  12. for (int i = tab.length ; i-- > 0 ;) {
  13. for (Entry<?,?> e = tab[i] ; e != null ; e = e.next) {
  14. if (e.value.equals(value)) {
  15. return true;
  16. }
  17. }
  18. }
  19. return false;
  20. }
  21. // 判断是否包含键值key
  22. public synchronized boolean containsKey(Object key) {
  23. Entry<?,?> tab[] = table;
  24. int hash = key.hashCode();
  25. int index = (hash & 0x7FFFFFFF) % tab.length;
  26. // index定位数组位置,for遍历链表查找元素
  27. for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
  28. if ((e.hash == hash) && e.key.equals(key)) {
  29. return true;
  30. }
  31. }
  32. return false;
  33. }

8 replace()方法

  1. public synchronized V replace(K key, V value) {
  2. Objects.requireNonNull(value);
  3. //根据键值查找元素
  4. Entry<?,?> tab[] = table;
  5. int hash = key.hashCode();
  6. int index = (hash & 0x7FFFFFFF) % tab.length;
  7. @SuppressWarnings("unchecked")
  8. Entry<K,V> e = (Entry<K,V>)tab[index];
  9. for (; e != null; e = e.next) {
  10. //查找成功,替换元素值
  11. if ((e.hash == hash) && e.key.equals(key)) {
  12. V oldValue = e.value;
  13. e.value = value;
  14. return oldValue;
  15. }
  16. }
  17. return null;
  18. }

9 remove()方法

  1. //根据键值删除元素,返回被删除元素值
  2. public synchronized V remove(Object key) {
  3. Entry<?,?> tab[] = table;
  4. int hash = key.hashCode();
  5. int index = (hash & 0x7FFFFFFF) % tab.length;
  6. @SuppressWarnings("unchecked")
  7. Entry<K,V> e = (Entry<K,V>)tab[index];
  8. //for遍历链表查找元素
  9. for(Entry<K,V> prev = null ; e != null ; prev = e, e = e.next) {
  10. //查找到元素进行链表的节点删除操作
  11. if ((e.hash == hash) && e.key.equals(key)) {
  12. modCount++;
  13. if (prev != null) {
  14. prev.next = e.next;
  15. } else {
  16. tab[index] = e.next;
  17. }
  18. count--;
  19. V oldValue = e.value;
  20. e.value = null;
  21. return oldValue;
  22. }
  23. }
  24. return null;
  25. }

10 elements()方法

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

11 小结

Hashtable是一个散列表,它存储的内容是键值对(key-value)映射。Hashtable不允许null对象。Hashtable中的方法使用了synchronized关键字修饰,因此Hashtable是线程安全的。

发表评论

表情:
评论列表 (有 0 条评论,75人围观)

还没有评论,来说两句吧...

相关阅读

    相关 STL之hashtable详解

    hash介绍 前面讲解二叉树实现键和值的存储。平衡二叉树就是插入和搜寻的速度非常快。那么我们是否可以使用一种方法,将键和值都存在在一段连续的空间内呢?可以,我们只需要将键

    相关 jdk源码-Map与HashTable

    Map map是一个接口,是一个映射着key和value关系的容器,从定义上看,map不能包含重复的key,一个key最多只能映射一个value。map是否有序取决于它的