HashMap 和 HashTable 的区别

1、继承的父类不同

public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, java.io.Serializable
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
2、 线程安全性不同

HashTable 部分方法上有自己的 synchronize 同步,是线程安全的。

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 = key.hashCode();
	int index = (hash & 0x7FFFFFFF) % tab.length;
	@SuppressWarnings("unchecked")
	Entry<K,V> entry = (Entry<K,V>)tab[index];
	for(; entry != null ; entry = entry.next) {
		if ((entry.hash == hash) && entry.key.equals(key)) {
			V old = entry.value;
			entry.value = value;
			return old;
		}
	}

	addEntry(hash, key, value, index);
	return null;
}

public synchronized V remove(Object key) {
	Entry<?,?> tab[] = table;
	int hash = key.hashCode();
	int index = (hash & 0x7FFFFFFF) % tab.length;
	@SuppressWarnings("unchecked")
	Entry<K,V> e = (Entry<K,V>)tab[index];
	for(Entry<K,V> 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;
}

public synchronized void putAll(Map<? extends K, ? extends V> t) {
	for (Map.Entry<? extends K, ? extends V> e : t.entrySet())
		put(e.getKey(), e.getValue());
}

public synchronized void clear() {
	Entry<?,?> tab[] = table;
	modCount++;
	for (int index = tab.length; --index >= 0; )
		tab[index] = null;
	count = 0;
}

public synchronized Object clone() {
	try {
		Hashtable<?,?> t = (Hashtable<?,?>)super.clone();
		t.table = new Entry<?,?>[table.length];
		for (int i = table.length ; i-- > 0 ; ) {
			t.table[i] = (table[i] != null)
				? (Entry<?,?>) table[i].clone() : null;
		}
		t.keySet = null;
		t.entrySet = null;
		t.values = null;
		t.modCount = 0;
		return t;
	} catch (CloneNotSupportedException e) {
		// this shouldn't happen, since we are Cloneable
		throw new InternalError(e);
	}
}

HashMap 可能导致数据丢失

// 新增Entry操作
// 会覆盖bucketIndex位置数据      
void addEntry(int hash, K key, V value, int bucketIndex) {      
	// 保存“bucketIndex”位置的值到“e”中      
	Entry<K,V> e = table[bucketIndex];      
	// 设置“bucketIndex”位置的元素为“新Entry”,      
	// 设置“e”为“新Entry的下一个节点”      
	table[bucketIndex] = new Entry<K,V>(hash, key, value, e);      
	// 若HashMap的实际大小 不小于 “阈值”,则调整HashMap的大小      
	if (size++ >= threshold)      
		resize(2 * table.length);      
}  

// 删除key操作
// 取得现在状态下该位置存储的头结点,进行计算操作,之后再把结果写会到该数组位置去,覆盖其他线程修改的数据
final Entry<K,V> removeEntryForKey(Object key) {      
	// 获取哈希值。若key为null,则哈希值为0;否则调用hash()进行计算      
	int hash = (key == null) ? 0 : hash(key.hashCode());      
	int i = indexFor(hash, table.length);      
	Entry<K,V> prev = table[i];      
	Entry<K,V> e = prev;      
 
	// 删除链表中“键为key”的元素      
	// 本质是“删除单向链表中的节点”      
	while (e != null) {      
		Entry<K,V> next = e.next;      
		Object k;      
		if (e.hash == hash &&      
			((k = e.key) == key || (key != null && key.equals(k)))) {      
			modCount++;      
			size--;      
			if (prev == e)      
				table[i] = next;      
			else     
				prev.next = next;      
			e.recordRemoval(this);      
			return e;      
		}      
		prev = e;      
		e = next;      
	}      
 
	return e;      
}  

// 扩容table操作  
// 同时赋给table扩容后的数组
void resize(int newCapacity) {      
	Entry[] oldTable = table;      
	int oldCapacity = oldTable.length;     
	//如果就容量已经达到了最大值,则不能再扩容,直接返回    
	if (oldCapacity == MAXIMUM_CAPACITY) {      
		threshold = Integer.MAX_VALUE;      
		return;      
	}      
 
	// 新建一个HashMap,将“旧HashMap”的全部元素添加到“新HashMap”中,      
	// 然后,将“新HashMap”赋值给“旧HashMap”。      
	Entry[] newTable = new Entry[newCapacity];      
	transfer(newTable);      
	table = newTable;      
	threshold = (int)(newCapacity * loadFactor);      
}  
3、 HashMap允许key,value为null,HashTable都不允许为null
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 = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> entry = (Entry<K,V>)tab[index];
        for(; entry != null ; entry = entry.next) {
            if ((entry.hash == hash) && entry.key.equals(key)) {
                V old = entry.value;
                entry.value = value;
                return old;
            }
        }

        addEntry(hash, key, value, index);
        return null;
    }

  4、是否提供contains方法

 HashMap把contains方法去掉了,改成containsValue和containsKey。

 Hashtable则保留了contains,containsValue和containsKey三个方法,其中containsKey和containsValue功能相同。

// 判断Hashtable是否包含“值(value)”      
public synchronized boolean contains(Object value) {      
 //注意,Hashtable中的value不能是null,      
 // 若是null的话,抛出异常!      
 if (value == null) {      
	 throw new NullPointerException();      
 }      

 // 从后向前遍历table数组中的元素(Entry)      
 // 对于每个Entry(单向链表),逐个遍历,判断节点的值是否等于value      
 Entry tab[] = table;      
 for (int i = tab.length ; i-- > 0 ;) {      
	 for (Entry<K,V> e = tab[i] ; e != null ; e = e.next) {      
		 if (e.value.equals(value)) {      
			 return true;      
		 }      
	 }      
 }      
 return false;      
}  

 5、两个遍历方式的内部实现上不同

      Hashtable、HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。

6、hash值不同

      哈希值的使用不同,HashTable直接使用对象的hashCode。而HashMap重新计算hash值。

      hashCode是jdk根据对象的地址或者字符串或者数字算出来的int类型的数值。

      Hashtable计算hash值,直接用key的hashCode(),在求hash值对应的位置索引时,用取模运算;
      HashMap 上一章节详细阐述了用key的hashCode(),进行hash函数计算出的值,通过indexForh&(length-1)保证获取的index一定在数组范围内。
        

7、内部实现使用的数组初始化和扩容方式不同

     HashTable在不指定容量的情况下的默认容量为11,增加的方式是 old*2+1;
     HashMap在不指定容量的情况下的默认容量为16, 增加的方式要求一定为2的次幂。比如16的二进制表示为 10000,那么length-1就是15,二进制为01111,同理扩容后的数组长度为32,



posted @ 2018-04-08 14:21  91vincent  阅读(142)  评论(0编辑  收藏  举报