先來講說這二者之間的不一樣:java
1.Hashtable 是JDK1.2出現的, 父類繼承Dictionary 實現的是Map, HashMap父類是AbstractMap實現的Map 數組
public class Hashtable extends Dictionary implements Map public class HashMap extends AbstractMap implements Map
2. Hashtable中的方法都是同步的, HashMap中的方法都是非同步的, 安全
因此從性能上來講HashMap的效率比Hashtable快, 那麼若是在多線程併發的環境下,HashMap如何實現同步處理 多線程
能夠經過 : 併發
Collections.synchronizedMap();
來實現線程同步, 看下synchronizedMap內部實現:源碼分析
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) { return new SynchronizedMap<>(m); } private static class SynchronizedMap<K,V> implements Map<K,V>, Serializable { private static final long serialVersionUID = 1978198479659022715L; private final Map<K,V> m; // Backing Map final Object mutex; // Object on which to synchronize SynchronizedMap(Map<K,V> m) { if (m==null) throw new NullPointerException(); this.m = m; mutex = this; } } SynchronizedMap(Map<K,V> m, Object mutex) { this.m = m; this.mutex = mutex; }
3.Hashtable中key和value都不能爲null, 在HashMap中, key能夠爲null,但這樣的健值只有一個, Hashtable源代碼:性能
public Object put(Object key, Object value) { // Make sure the value is not null if (value == null) throw new NullPointerException(); }
4. 二者遍歷的方式的內部實現不一樣, HashMap使用了Iterator, Hashtable使用了Enumeration的方式this
5. 哈希值的使用不一樣, Hashtable直接使用對象的hashcode, HashMap從新計算hash值spa
6. HashTable中hash數組默認大小爲11, 增長的方式是old*2+1, HashMap數組的默認大小是16, 並且必定是2的指數 線程
那麼接下來咱們來分析一下HashMap的內部結構:
HashMap是一個數組和鏈表的組合體, 內部以下圖:
源代碼分析:
public V put(K key, V value) { if (table == EMPTY_TABLE) { inflateTable(threshold); } if (key == null) return putForNullKey(value); int hash = hash(key); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; }
當往HashMap中put元素的時候,先根據key的hash值獲得這個元素在數組中的位置(即下標),而後就能夠把這個元素放到對應的位置中了。若是這個元素所在的位子上已經存放有其餘元素了,那麼在同一個位子上的元素將以鏈表的形式存放,新加入的放在鏈頭,最早加入的放在鏈尾
前面咱們有說到,HashMap不是線程安全的, 那麼在多線程環境下, 咱們應該如何保證線程安全呢, 在JDK1.5以後咱們引入ConcurrentHashMap類:
public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>, Serializable { }
那麼ConcurrentHashMap是具體如何實現線程安全的,從源代碼中能夠看出它引入了一個叫「分段鎖」的概念,具體能夠理解成將Map劃分紅N個小的Hashtable, 根據key.hashCode()來決定把key放到哪一個HashTable中
在ConcurrentHashMap中,就是把Map分紅N個Segment,put和get的時候, 根據key.hashCode() 算出放到哪一個Segment中
public V put(K key, V value) { Segment<K,V> s; if (value == null) throw new NullPointerException(); int hash = hash(key); int j = (hash >>> segmentShift) & segmentMask; if ((s = (Segment<K,V>)UNSAFE.getObject // nonvolatile; recheck (segments, (j << SSHIFT) + SBASE)) == null) // in ensureSegment s = ensureSegment(j); return s.put(key, hash, value, false); } public V get(Object key) { Segment<K,V> s; // manually integrate access methods to reduce overhead HashEntry<K,V>[] tab; int h = hash(key); long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE; if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null && (tab = s.table) != null) { for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE); e != null; e = e.next) { K k; if ((k = e.key) == key || (e.hash == h && key.equals(k))) return e.value; } } return null; }
以上就是ConcurrentHashMap的工做機制, 經過把整個Map劃分爲N個Segment,能夠提供相同的線程安全,效率提高N倍,默認提高16倍