HashMap和Hashtable的6個區(qū)別,最后一個沒幾個人知道!
HashMap 和 Hashtable 是 Java 開發(fā)程序員必須要掌握的,也是在各種 Java 面試場合中必須會問到的。
但你對這兩者的區(qū)別了解有多少呢?
現(xiàn)在,棧長我給大家總結(jié)一下,或許有你不明朗的地方,在棧長的指點下都會撥開迷霧見晴天。
1、線程安全
Hashtable 是線程安全的,HashMap 不是線程安全的。
為什么說 HashTable 是線程安全的?
來看下 Hashtable 的源碼,Hashtable 所有的元素操作都是 synchronized 修飾的,而 HashMap 并沒有。
- public synchronized V put(K key, V value);
- public synchronized V get(Object key);
- ...
2、性能優(yōu)劣
既然 Hashtable 是線程安全的,每個方法都要阻塞其他線程,所以 Hashtable 性能較差,HashMap 性能較好,使用更廣。
如果要線程安全又要保證性能,建議使用 JUC 包下的 ConcurrentHashMap。
3、NULL
Hashtable 是不允許鍵或值為 null 的,HashMap 的鍵值則都可以為 null。
那么問題來了,為什么 Hashtable 是不允許 KEY 和 VALUE 為 null, 而 HashMap 則可以?
Hashtable 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 = key.hashCode();
- ...
- }
HashMap hash 方法邏輯:
- static final int hash(Object key) {
- int h;
- return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
- }
可以看出 Hashtable key 為 null 會直接拋出空指針異常,value 為 null 手動拋出空指針異常,而 HashMap 的邏輯對 null 作了特殊處理。
4、實現(xiàn)方式
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 類。
Dictionary 是 JDK 1.0 添加的,貌似沒人用過這個,棧長我也沒用過。。
5、容量擴(kuò)容
HashMap 的初始容量為:16,Hashtable 初始容量為:11,兩者的負(fù)載因子默認(rèn)都是: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
- }
當(dāng)現(xiàn)有容量大于總?cè)萘?* 負(fù)載因子時,HashMap 擴(kuò)容規(guī)則為當(dāng)前容量翻倍,Hashtable 擴(kuò)容規(guī)則為當(dāng)前容量翻倍 + 1。
6、迭代器
HashMap 中的 Iterator 迭代器是 fail-fast 的,而 Hashtable 的 Enumerator 不是 fail-fast 的。
所以,當(dāng)其他線程改變了HashMap 的結(jié)構(gòu),如:增加、刪除元素,將會拋出 ConcurrentModificationException 異常,而 Hashtable 則不會。
可以來看下這個區(qū)別的演示:
- /**
- * 微信公眾號:Java技術(shù)棧
- **/
- 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 的區(qū)別。