和 HashMap 一样,Hashtable 也是一个散列表,它存储的内容是键值对。
Hashtable 在 Java 中的定义为:
1 | public class Hashtable<K,V> |
从源码中我们可以看出:
- Hashtable继承于Dictionary类 —— 任何可将键值映射到相应值的类的一种抽象父类。每个键和值都是对象。
- 实现了Map<K,V>, Cloneable, java.io.Serializable 接口
成员变量
Hashtable是通过"拉链法"实现的哈希表。它包括几个重要的成员变量:table, count, threshold, loadFactor, modCount。
1 | /** |
- table是一个Entry[] 数组类型。而Entry实际上就是一个单向链表(在之前的Java-HashMap的原理探究一问涉及过)。哈希表的"key-value键值对"都是存储在Entry数组中的
- count是Hashtable的大小,它是Hashtable保存的键值对的数量
- threshold是Hashtable的阈值,用于判断是否需要调整Hashtable的容量。threshold = 容量*加载因子
- loadFactor 是加载因子
- modCount是用来实现fail-fast机制(在之前的Java-HashMap的原理探究一问涉及过)的
构造方法
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 具有相同映射关系的新哈希表。
1 | /** |
put方法
- 如果value为空,则抛出异常
- 计算key的hash值,并根据hash值获得key在table数组中的位置index。如果table[index]不为空,则进行迭代。如果遇到相同的key,则直接替换,并返回旧的value。
- 否则,将其插入到table[index]的位置。
1 | public synchronized V put(K key, V value) {// 注意synchronized关键字 |
通过一个实际的例子来演示一下这个过程:
假设我们现在Hashtable的容量为5,已经存在了(5,5),(13,13),(16,16),(17,17),(21,21)这 5 个键值对,目前他们在Hashtable中的位置如下:
现在,我们插入一个新的键值对,put(16,22),假设key=16的索引为1.但现在索引1的位置有两个Entry了,所以程序会对链表进行迭代。迭代的过程中,发现其中有一个Entry的key和我们要插入的键值对的key相同,所以现在会做的工作就是将newValue=22替换oldValue=16,然后返回oldValue=16.
然后我们现在再插入一个,put(33,33),key=33的索引为3,并且在链表中也不存在key=33的Entry,所以将该节点插入链表的第一个位置。
get方法
相比较于 put 方法,get 方法则简单很多。其过程就是首先通过 hash()方法求得 key 的哈希值,然后根据 hash 值得到 index 索引(上述两步所用的算法与 put 方法都相同)。然后迭代链表,返回匹配的 key 的对应的 value;找不到则返回 null。
1 | public synchronized V get(Object key) { // 注意synchronized关键字 |
Hashtable遍历方式
1 | //1、使用keys() |
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 对象,并把它作为一个封装的对象来返回。