一.前言
- HashMap和Hashtable大部分算法是相同的,容器学习一:HashMap源码分析 对HashMap源码进行了分析,可以先阅读它。
- 相同的算法部分不再分析,本文主要考虑Hashtable和HashMap的不同之处。
二.Hashtable成员变量
private transient Entry[] table;
// 等同于HashMap里面的size
private transient int count;
private int threshold;
private float loadFactor;
private transient int modCount = 0;
三.Hashtable构造函数
// DEFAULT_INITIAL_CAPACITY=11,DEFAULT_LOAD_FACTOR还是0.75
public Hashtable() {
this(11, 0.75f);
}
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;
// 直接用initialCapacity初始化,并没有要求用2的次方指来初始化
this.loadFactor = loadFactor;
table = new Entry[initialCapacity];
threshold = (int) (initialCapacity * loadFactor);
}
四.hash算法和index算法
//自己拿到key的hash值
int hash = key.hashCode();
//计算index做了求模运算。
int index = (hash & 0x7FFFFFFF) % tab.length;
五.取数据
public synchronized V get(Object key) {
Entry tab[] = table;
//Hashtable所有的方法都不接受null的key,所有的地方都是不判断就直接key.hashCode();
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<K, V> e = tab[index]; e != null; e = e.next) {
//同HahsMap相比没有判断e.key==key,
//Hahstable里面,e.key.equals(key)就够了,在key不为null的前提下e.key==key是e.key.equals(key)的充分不必要条件
if ((e.hash == hash) && e.key.equals(key)) {
return e.value;
}
}
return null;
}
六.存数据
public synchronized V put(K key, V value) {
if (value == null) {
throw new NullPointerException();
}
Entry tab[] = table;
int hash = key.hashCode();
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++;
//HashMap先加Entry再决定是否扩容,Hahstable再判断大小在加Entry
//我还是喜欢这样比较:count + 1 > threshold
if (count >= threshold) {
rehash();
tab = table;
index = (hash & 0x7FFFFFFF) % tab.length;
}
// Creates the new entry.
Entry<K, V> e = tab[index];
tab[index] = new Entry<K, V>(hash, key, value, e);
count++;
return null;
}
protected void rehash() {
int oldCapacity = table.length;
Entry[] oldMap = table;
int newCapacity = oldCapacity * 2 + 1;
Entry[] newMap = new Entry[newCapacity];
modCount++;
threshold = (int) (newCapacity * loadFactor);
table = newMap;
//和HashMap算法是一样的,建议看HashMap里面的写法好理解点。
for (int i = oldCapacity; i-- > 0;) {
for (Entry<K, V> old = oldMap[i]; old != null;) {
Entry<K, V> e = old;
old = old.next;
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = newMap[index];
newMap[index] = e;
}
}
}
七.总结
HashMap | Hashtable | |
出现时间 | JDK1.2,所以代码质量更高,更容易明白 | JDK1.0 |
并发控制 | 没有考虑并发 | 所有方法都加了synchronized,即使有些我认为不需要的也加了 |
是否接受值为null的Key 或Value
| 接受 | 不接收。put等方法里面:if (value == null) { 显示throw new NullPointerException(); };int hash=key.hashcode隐示throw NullPointerException(); |
初始化table | 缺省容量16。初始化时可以指定initial capacity,若不是2的次方,HashMap将选取第一个大于initial capacity 的2的次方值作为其初始长度 | 缺省容量11。初始化时可以指定initial capacity |
扩容 | 添加Entry后判断是否该扩容。扩容至2*oldCapacity | 先判断是否扩容再添加Entry。扩容至2*oldCapacity + 1 |
数据遍历的方式 | Iterator |
Iterator 和 Enumeration
|
是否支持fast-fail
| 支持fast-fail |
用Iterator遍历,支持fast-fail
用Enumeration不支持fast-fail.
|
hash算法和index算法 |
优于Hashtable,通过对Key的hash做移位运算和位的与运算,使其能更广泛地分散到数组的不同位置
|
当数组长度较小,并且Key的hash值低位数值分散不均匀时,不同的hash值计算得到相同下标值的几率较高
|
实现和继承 | extends AbstractMap 骨架结构的体现,代码质量上去了 | extends Dictionary implements Map 直接实现Map接口。多基础了一个已过时的经Dictionary类,就不用去管了 |