我们首先说一下有关HashTable的相关内容,HashMap的相关内容可以在上一篇博客中找到。。。
1、HashTable的特点
1)是以键值对的形式存在的
2)底层数据结构是数组+链表
3)key与value均不能为null
4)key 不可以重复,value可以重复
5)不能保证插入的顺序
6)线程安全
2、HashTable源码解读
1)继承关系
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable
Hashtable 继承于Dictionary,实现了Map、Cloneable、java.io.Serializable接口
2)基本属性
private transient Entry<K,V>[] table;//数组
private transient int count;//键值对的个数
private int threshold;//阈值
private float loadFactor;//加载因子
private transient int modCount = 0
3)扩容机制
protected void rehash() {
HashtableEntry e, old;
int i, index;
int oldCapacity = table.length;
HashtableEntry oldTable[] = table;
int newCapacity = oldCapacity * 2 + 1;
HashtableEntry newTable[] = new HashtableEntry[newCapacity];
threshold = (int)(newCapacity * loadFactor);
table = newTable;
for (i = oldCapacity ; i-- > 0 ;) {
for (old = oldTable[i] ; old != null ; ) {
e = old;
old = old.next;
index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = newTable[index];
newTable[index] = e;
}
}
}
class HashtableEntry {
int hash;
Object key;
Object value;
HashtableEntry next;
从源码可以看出HashMap的扩容机制是扩容到原数组的2倍+1
4)构造函数
HashMap的构造函数有四种
a.指定容量大小和加载因子的构造函数
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);
initHashSeedAsNeeded(initialCapacity);
}
b.指定“容量大小”的构造函数
public Hashtable(int initialCapacity) {
this(initialCapacity, 0.75f);
}
c.默认构造函数,指定的容量大小是11;加载因子是0.75
public Hashtable() {
this(11, 0.75f);
}
d.包含“子Map”的构造函数
public Hashtable(Map<? extends K, ? extends V> t) {
this(Math.max(2*t.size(), 11), 0.75f);
putAll(t);
}
5) get()方法:根据key值得到value
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;
}
6)remove()方法:根据Key删除整个键值对,返回的是该键值对的value值
public synchronized V remove(Object key) {
Entry tab[] = table;
int hash = hash(key);
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<K,V> e = tab[index], prev = null ; e != null ; prev = e, e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
modCount++;
if (prev != null) {
prev.next = e.next;
} else {
tab[index] = e.next;
}
count--;
V oldValue = e.value;
e.value = null;
return oldValue;
}
}
return null;
}
7)put()方法:添加键值对
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
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)) {
V old = e.value;
e.value = value;
return old;
}
}
modCount++;
if (count >= threshold) {
// Rehash the table if the threshold is exceeded
rehash();
tab = table;
hash = hash(key);
index = (hash & 0x7FFFFFFF) % tab.length;
}
// Creates the new entry.
Entry<K,V> e = tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
return null;
}
3、HashTable与HashMap的异同点
不同点:
1)继承的类不同
HashTable继承Dictionary
HashMap继承AbstractMap
2)初始数组大小不同
HashTable:11
HashMap:16
3)线程安全问题
HashTable:线程安全
HashMap:非线程安全
HashTable方法是同步的,而HashMap则不是。HashTable中几乎所有的public的方法都是synchronized 的,而有些方法也是在内部通过 synchronized 代码块来实现。
4)扩容的方式不同
HashTable:旧数组的2倍+1
HashMap:旧数组的2倍
HashMap的容量必须是2的指数级大小数据。当初始容量给定时,一般会自动转换成离它最近的一个2的指数的数字,作为它的容量。
5)null值处理不同
HashTable:不允许key与value为null,当用null当做Key的时候会抛出java.lang.NullPointerException异常,因为其在计算Hash的时候调用了key.hashCode();当Key为Null的时候就会抛出异常,而且在put中,并未单独对null进行判断(key不能重复)
HashMap:key可以为null,有且只有一个,value可以由多个为null。
6)hash函数不同
HashTable
private int hash(Object k) {
// hashSeed will be zero if alternative hashing is disabled.
return hashSeed ^ k.hashCode();
}
HashMap
final int hash(Object k) {
int h = hashSeed;
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h ^= k.hashCode();
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
7)支持的遍历种类不同
HashMap只支持Iterator(迭代器)遍历。
而Hashtable支持Iterator(迭代器)和Enumeration(枚举器)两种方式遍历
8) contains()方法
Hashtable支持contains(Object value)方法,而且重写了toString()方法;
而HashMap不支持contains(Object value)方法,没有重写toString()方法。
相同点:
1)底层均是数组+链表
2)扩容时机相同