先来讲说这二者之间的不一样: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倍