1.Hashtable与HashMap区别比较
先来说说这两者之间的不同:
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的方式
5. 哈希值的使用不同, Hashtable直接使用对象的hashcode, HashMap重新计算hash值
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值得到这个元素在数组中的位置(即下标),然后就可以把这个元素放到对应的位置中了。如果这个元素所在的位子上已经存放有其他元素了,那么在同一个位子上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾
2.HashMap与ConcurrentHashMap源码分析
前面我们有说到,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倍