HashMap和HashTable区别
1,线程安全
Hashtable是线程安全的,HashMap不是线程安全的。
为什么说HashTable是线程安全的?
来看下Hashtable的源码,Hashtable所有的元素操作都是synchronized修饰的,而HashMap并没有。
public synchronized V put(K key, V value);
public synchronized V get(Object key);
...
2,性能优劣
既然Hashtable是线程安全的,每个方法都要阻塞其他线程,所以Hashtable性能较差,HashMap性能较好,使用更广。
如果要线程安全又要保证性能,建议使用JUC包下的ConcurrentHashMap。
3,NULL
Hashtable是不允许键或值为null的,HashMap的键值则都可以为null。
那么问题来了,为什么Hashtable是不允许KEY和VALUE为null,而HashMap则可以?
Hashtable方法逻辑:
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(); ... }
HashMap hash方法逻辑:
static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
可以看出Hashtable key为null会直接抛出空指针异常,值为null手动抛出空指针异常,而HashMap的逻辑对null作了特殊处理。
4,实现方式
Hashtable的继承源码:
public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, java.io.Serializable
HashMap的继承源码:
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
可以看出两者继承的类不一样,Hashtable继承了Dictionary类,而HashMap继承的是AbstractMap类。
5,容量扩容
HashMap的初始容量为:16,Hashtable初始容量为:11,两者的负载因子默认都是:0.75。
/** * Constructs a new, empty hashtable with a default initial capacity (11) * and load factor (0.75). */ public Hashtable() { this(11, 0.75f); } /** * Constructs an empty <tt>HashMap</tt> with the default initial capacity * (16) and the default load factor (0.75). */ public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted }
当前容量大于总容量*负载因子时,HashMap扩容规则为当前容量翻倍,Hashtable扩容规则为当前容量翻倍+ 1。
6,迭代器
HashMap中的Iterator迭代器是fail-fast的,而Hashtable的Enumerator不是fail-fast的。
所以,当其他线程改变了HashMap的结构,如:增加,删除元素,将会抛出ConcurrentModificationException异常,而Hashtable则不会。
可以来看下这个区别的演示:
public static void main(String[] args) { Map<String, String> hashtable = new Hashtable<>(); hashtable.put("t1", "1"); hashtable.put("t2", "2"); hashtable.put("t3", "3"); Enumeration<Map.Entry<String, String>> iterator1 = (Enumeration<Map.Entry<String, String>>) hashtable.entrySet().iterator(); hashtable.remove(iterator1.nextElement().getKey()); while (iterator1.hasMoreElements()) { System.out.println(iterator1.nextElement()); } Map<String, String> hashMap = new HashMap<>(); hashMap.put("h1", "1"); hashMap.put("h2", "2"); hashMap.put("h3", "3"); Iterator<Map.Entry<String, String>> iterator2 = hashMap.entrySet().iterator(); hashMap.remove(iterator2.next().getKey()); while (iterator2.hasNext()) { System.out.println(iterator2.next()); } }
输出信息:
t2=2
t1=1
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextNode(HashMap.java:1442)
at java.util.HashMap$EntryIterator.next(HashMap.java:1476)
at java.util.HashMap$EntryIterator.next(HashMap.java:1474)
at cn.javastack.Test.main(Test.java:37)
看到了吧?
所以,这条同样也是Enumeration和Iterator的区别。