概述
和 HashMap 一样,Hashtable 也是一个散列表,它存储的内容是键值对。

Hashtable 在 Java 中的定义为:

public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable{}
从源码中,我们可以看出,Hashtable 继承于 Dictionary 类,实现了 Map, Cloneable, java.io.Serializable接口。其中Dictionary类是任何可将键映射到相应值的类(如 Hashtable)的抽象父类,每个键和值都是对象(源码注释为:The Dictionary class is the abstract parent of any class, such as Hashtable, which maps keys to values. Every key and every value is an object.)。但在这一点我开始有点怀疑,因为我查看了HashMap以及TreeMap的源码,都没有继承于这个类。不过当我看到注释中的解释也就明白了,其 Dictionary 源码注释是这样的:NOTE: This class is obsolete. New implementations should implement the Map interface, rather than extending this class. 该话指出 Dictionary 这个类过时了,新的实现类应该实现Map接口。

Hashtable 源码解读
成员变量
Hashtable是通过"拉链法"实现的哈希表。它包括几个重要的成员变量:table, count, threshold, loadFactor, modCount。

table是一个 Entry[] 数组类型,而 Entry(在 HashMap 中有讲解过)实际上就是一个单向链表。哈希表的"key-value键值对"都是存储在Entry数组中的。
count 是 Hashtable 的大小,它是 Hashtable 保存的键值对的数量。
threshold 是 Hashtable 的阈值,用于判断是否需要调整 Hashtable 的容量。threshold 的值=“容量*加载因子”。
loadFactor 就是加载因子。
modCount 是用来实现 fail-fast 机制的。
关于变量的解释在源码注释中都有,最好还是应该看英文注释。

/**
* The hash table data.
*/
private transient Entry<K,V>[] table;

/*** The total number of entries in the hash table.*/
private transient int count;/*** The table is rehashed when its size exceeds this threshold.  (The* value of this field is (int)(capacity * loadFactor).)** @serial*/
private int threshold;/*** The load factor for the hashtable.** @serial*/
private float loadFactor;/*** The number of times this Hashtable has been structurally modified* Structural modifications are those that change the number of entries in* the Hashtable or otherwise modify its internal structure (e.g.,* rehash).  This field is used to make iterators on Collection-views of* the Hashtable fail-fast.  (See ConcurrentModificationException).*/
private transient int modCount = 0;

构造方法
Hashtable 一共提供了 4 个构造方法:

public Hashtable(int initialCapacity, float loadFactor): 用指定初始容量和指定加载因子构造一个新的空哈希表。useAltHashing 为 boolean,其如果为真,则执行另一散列的字符串键,以减少由于弱哈希计算导致的哈希冲突的发生。
public Hashtable(int initialCapacity):用指定初始容量和默认的加载因子 (0.75) 构造一个新的空哈希表。
public Hashtable():默认构造函数,容量为 11,加载因子为 0.75。
public Hashtable(Map<? extends K, ? extends V> t):构造一个与给定的 Map 具有相同映射关系的新哈希表。
/**
* Constructs a new, empty hashtable with the specified initial
* capacity and the specified load factor.
*
* @param initialCapacity the initial capacity of the hashtable.
* @param loadFactor the load factor of the hashtable.
* @exception IllegalArgumentException if the initial capacity is less
* than zero, or if the load factor is nonpositive.
*/
public Hashtable(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal Load: "+loadFactor);

    if (initialCapacity==0)initialCapacity = 1;this.loadFactor = loadFactor;table = new Entry[initialCapacity];threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);useAltHashing = sun.misc.VM.isBooted() &&(initialCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
}/*** Constructs a new, empty hashtable with the specified initial capacity* and default load factor (0.75).** @param     initialCapacity   the initial capacity of the hashtable.* @exception IllegalArgumentException if the initial capacity is less*              than zero.*/
public Hashtable(int initialCapacity) {this(initialCapacity, 0.75f);
}/*** Constructs a new, empty hashtable with a default initial capacity (11)* and load factor (0.75).*/
public Hashtable() {this(11, 0.75f);
}/*** Constructs a new hashtable with the same mappings as the given* Map.  The hashtable is created with an initial capacity sufficient to* hold the mappings in the given Map and a default load factor (0.75).** @param t the map whose mappings are to be placed in this map.* @throws NullPointerException if the specified map is null.* @since   1.2*/
public Hashtable(Map<? extends K, ? extends V> t) {this(Math.max(2*t.size(), 11), 0.75f);putAll(t);
}

put 方法
put 方法的整个流程为:

判断 value 是否为空,为空则抛出异常;
计算 key 的 hash 值,并根据 hash 值获得 key 在 table 数组中的位置 index,如果 table[index] 元素不为空,则进行迭代,如果遇到相同的 key,则直接替换,并返回旧 value;
否则,我们可以将其插入到 table[index] 位置。
我在下面的代码中也进行了一些注释:

public synchronized V put(K key, V value) {
// Make sure the value is not null确保value不为null
if (value == null) {
throw new NullPointerException();
}

    // Makes sure the key is not already in the hashtable.//确保key不在hashtable中//首先,通过hash方法计算key的哈希值,并计算得出index值,确定其在table[]中的位置//其次,迭代index索引位置的链表,如果该位置处的链表存在相同的key,则替换value,返回旧的valueEntry tab[] = table;int hash = hash(key);int index = (hash & 0x7FFFFFFF) % tab.length;for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {if ((e.hash == hash) && e.key.equals(key)) {V old = e.value;e.value = value;return old;}}modCount++;if (count >= threshold) {// Rehash the table if the threshold is exceeded//如果超过阀值,就进行rehash操作rehash();tab = table;hash = hash(key);index = (hash & 0x7FFFFFFF) % tab.length;}// Creates the new entry.//将值插入,返回的为nullEntry<K,V> e = tab[index];// 创建新的Entry节点,并将新的Entry插入Hashtable的index位置,并设置e为新的Entry的下一个元素tab[index] = new Entry<>(hash, key, value, e);count++;return null;
}

通过一个实际的例子来演示一下这个过程:

假设我们现在Hashtable的容量为5,已经存在了(5,5),(13,13),(16,16),(17,17),(21,21)这 5 个键值对,目前他们在Hashtable中的位置如下:

图1

现在,我们插入一个新的键值对,put(16,22),假设key=16的索引为1.但现在索引1的位置有两个Entry了,所以程序会对链表进行迭代。迭代的过程中,发现其中有一个Entry的key和我们要插入的键值对的key相同,所以现在会做的工作就是将newValue=22替换oldValue=16,然后返回oldValue=16.

图2

然后我们现在再插入一个,put(33,33),key=33的索引为3,并且在链表中也不存在key=33的Entry,所以将该节点插入链表的第一个位置。

图3

get 方法
相比较于 put 方法,get 方法则简单很多。其过程就是首先通过 hash()方法求得 key 的哈希值,然后根据 hash 值得到 index 索引(上述两步所用的算法与 put 方法都相同)。然后迭代链表,返回匹配的 key 的对应的 value;找不到则返回 null。

public synchronized V get(Object key) {
Entry tab[] = table;
int hash = hash(key);
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
return e.value;
}
}
return null;
}
Hashtable 遍历方式
Hashtable 有多种遍历方式:

//1、使用keys()
Enumeration en1 = table.keys();
while(en1.hasMoreElements()) {
en1.nextElement();
}

//2、使用elements()
Enumeration en2 = table.elements();
while(en2.hasMoreElements()) {
en2.nextElement();
}

//3、使用keySet()
Iterator it1 = table.keySet().iterator();
while(it1.hasNext()) {
it1.next();
}

//4、使用entrySet()
Iterator<Entry<String, String>> it2 = table.entrySet().iterator();
while(it2.hasNext()) {
it2.next();
}
Hashtable 与 HashMap 的简单比较
HashTable 基于 Dictionary 类,而 HashMap 是基于 AbstractMap。Dictionary 是任何可将键映射到相应值的类的抽象父类,而 AbstractMap 是基于 Map 接口的实现,它以最大限度地减少实现此接口所需的工作。
HashMap 的 key 和 value 都允许为 null,而 Hashtable 的 key 和 value 都不允许为 null。HashMap 遇到 key 为 null 的时候,调用 putForNullKey 方法进行处理,而对 value 没有处理;Hashtable遇到 null,直接返回 NullPointerException。
Hashtable 方法是同步,而HashMap则不是。我们可以看一下源码,Hashtable 中的几乎所有的 public 的方法都是 synchronized 的,而有些方法也是在内部通过 synchronized 代码块来实现。所以有人一般都建议如果是涉及到多线程同步时采用 HashTable,没有涉及就采用 HashMap,但是在 Collections 类中存在一个静态方法:synchronizedMap(),该方法创建了一个线程安全的 Map 对象,并把它作为一个封装的对象来返回。

Hashtable 的实现原理相关推荐

  1. hashtable,dictionary 从原理上说说有什么异同,哪个性能高一些

    hashtable里存的对象全部是object类型 ,所有对象存进去都被转成object类型,读取出来每次都需要转换类型,hashtable对存入的类型没有限制 , 因此在读取转换类型时容易出错, d ...

  2. 深入Java集合学习系列:Hashtable的实现原理

    第1部分 Hashtable介绍 和HashMap一样,Hashtable也是一个散列表,它存储的内容是键值对(key-value)映射.Hashtable继承于Dictionary,实现了Map.C ...

  3. Redis源码-Set:Redis Set存储原理、Redis Set集合操作命令、Redis Set两种存储底层编码intset+hashtable、Redis Set应用场景

    Redis源码-Set:Redis Set存储原理.Redis Set集合操作命令.Redis Set两种存储底层编码intset+hashtable.Redis Set应用场景 Redis数据类型 ...

  4. 哈希表(HASHTABLE)

    原文地址:http://www.cnblogs.com/abatei/archive/2009/06/23/1509790.html C#中实现了哈希表数据结构的集合类有: (1) System.Co ...

  5. Java数据结构和算法:HashMap的实现原理

    HashMap源码理解 Java集合之HashMap HashMap原理及实现学习总结 HashMap源码分析 HashMap原理及实现学习总结 1. HashMap概述 HashMap是基于哈希表的 ...

  6. 除留余数法构造哈希表_哈希表算法原理

    基本概念 哈希表(Hash Table)是一种根据关键字直接访问内存存储位置的数据结构.通过哈希表,数据元素的存放位置和数据元素的关键字之间建立起某种对应关系,建立这种对应关系的函数称为哈希函数. 哈 ...

  7. 不用 Python 自带的 Dict 实现自己的 HashTable

    这个题目其实源于很久之前的一次 Uber 面试,码工换工作无非就是刷 leetcode ,研究如何翻转二叉树之类的算法问题,所以头一次在电话里听到这道题的时候还是挺耳目一新的.当时顺利写出来了,也通过 ...

  8. java集合框架(二):HashTable

    2019独角兽企业重金招聘Python工程师标准>>> HashTable作为集合框架中的一员,现在是很少使用了,一般都是在面试中会问到其与HashMap的区别.为了能在求职的时候用 ...

  9. HashMap、ConcurrentHashMap原理分析

    集合(Collection)是编程中常用的数据结构,而并发也是服务器端编程常用的技术之一,并发总是离不开集合这类高级数据结构的支持.比如两个线程需要同时访问一个中间临界区(Queue),比如常会用缓存 ...

最新文章

  1. mysql self join_mysql self join的实现--left join 和inner join
  2. X11: Linux跨网络运行XWindow程序
  3. python爬虫采集网站数据
  4. block的用法以及block和delegate的比较(转发)
  5. html 字符串最后加空格,js给字符串每个字符中间加空格
  6. Swagger自动接口文档生成框架————springboot整合swagger总结
  7. 一文读懂商业智能(BI):企业数据分析的中枢
  8. 用remastersys备份LINUX,注意备份盘的空间占用
  9. 74系列芯片功能说明
  10. 联想ghost重装系统_联想笔记本重装系统图文教程
  11. python nlp文本摘要_NLP(十一) 提取文本摘要
  12. 估值指标一把手——市盈率
  13. PHP 毫秒级时间戳生成
  14. Activiti7框架学习
  15. java 日历计算农历和节假日的常用类(包括除夕的算法)
  16. 用python画四瓣树叶_使用Python turtle画分形树叶图
  17. 一段话中手机号中间四位做特殊处理
  18. OpenGL(可编程管线):Bezier曲面
  19. 浅议大数据时代下消防工作、生活和思维的变革
  20. 计算机网络基础 第二章

热门文章

  1. Error loading Python DLL python27.dll (error code 14001)
  2. JumpServer 安装部署
  3. 检查 Docker 版本,重装 Docker
  4. 生日祝福html_每周便当| 魔法便当盒里的生日蛋糕,祝佳佳八岁生日快乐!
  5. vc 界面编程常用方法(http://blog.emuch.net/244485/spacelist-blog-itemtypeid-2708.html)
  6. 怎样在Word任意图形插入图片
  7. 谷歌获取货币汇率代码
  8. GB28181协议--SIP协议介绍
  9. 基于IDEA的Java学生管理系统
  10. Qt 使用 MPV 开源播放器