HashMap和HashTable的区别一种比较简单的回答是:
(1)HashMap是非线程安全的,HashTable是线程安全的。
(2)HashMap的键和值都允许有null存在,而HashTable则都不行。
(3)因为线程安全、哈希效率的问题,HashMap效率比HashTable的要高。
JAVA的数据结构存储:
1、数组:查询快,增删慢;连续空间寻址快。
2、链表:增删快,查询慢;空间不连续,增删只需修改指针。
Hash表结构:
从下图中,我们可以发现哈希表是由数组+链表组成的,一个长度为16的数组中,每个元素存储的是一个链表的头结点,通过功能类似于hash(key.hashCode())%len的操作,获得要添加的元素所要存放的的数组位置。
HashMap的哈希算法实际操作是通过位运算,比取模运算效率更高,同样能达到使其分布均匀的目的,后面会介绍。
键值对所存放的数据结构其实是HashMap中定义的一个Entity内部类,数组来实现的,属性有key、value和指向下一个Entity的next。
HashMap的实现重点需要注意的在两个方面,一个是链表结构,一个是table的resize()扩容;
HashMap和HashTable的对比:
HashTable和HashMap采用相同的存储机制,二者的实现基本一致,不同的是:
(1)HashMap是非线程安全的,HashTable是线程安全的,内部的方法基本都经过synchronized修饰。
(2)因为同步、哈希性能等原因,性能肯定是HashMap更佳,因此HashTable已被淘汰。
(3) HashMap允许有null值的存在,而在HashTable中put进的键值只要有一个null,直接抛出NullPointerException。
(4)HashMap默认初始化数组的大小为16,HashTable为11。前者扩容时乘2,使用位运算取得哈希,效率高于取模。而后者为乘2加1,都是素数和奇数,这样取模哈希结果更均匀。
言归正传,看下两种集合的hash算法。看源码也不难理解。
//HashMap的散列函数,这里传入参数为键值对的key
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
//返回hash值的索引,h & (length-1)操作等价于 hash % length操作, 但&操作性能更优
static int indexFor(int h, int length) {
// length must be a non-zero power of 2
return h & (length-1);
}
//HashTable的散列函数直接在put方法里实现了
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
ConcurrentHashMap原理:
ConcurrentHashMap引入了分割(Segment),上面代码中的最后一行其实就可以理解为把一个大的Map拆分成N个小的HashTable,在put方法中,会根据hash(paramK.hashCode())来决定具体存放进哪个Segment,如果查看Segment的put操作,我们会发现内部使用的同步机制是基于lock操作的,这样就可以对Map的一部分(Segment)进行上锁,这样影响的只是将要放入同一个Segment的元素的put操作,保证同步的时候,锁住的不是整个Map(HashTable就是这么做的),相对于HashTable提高了多线程环境下的性能,因此HashTable已经被淘汰了。
HashMap和ConCurrentHashMap的对比
最后对这俩兄弟做个区别总结吧:
(1)经过4.2的分析,我们知道ConcurrentHashMap对整个桶数组进行了分割分段(Segment),然后在每一个分段上都用lock锁进行保护,相对于HashTable的syn关键字锁的粒度更精细了一些,并发性能更好,而HashMap没有锁机制,不是线程安全的。
(2)HashMap的键值对允许有null,但是ConCurrentHashMap都不允许。
所以 HashMap 与 ConcurrentHashMap 区别为 CHM部分方法具备原子性,CHM原子性 基于CAS实现,CAS是乐观锁实现同步的一种方式
使用concurrentHashMap随便操作是原子性,但是放一起可能违反happen-before规则,chm是弱一致,要想强一致必须使用全局锁:
建议使用以下代码
Map map = Collections.synchronizedMap(newHashMap());
String getString(String name) {
synchronized(map){//可保证该同步块内的所有代码对map是一个原子操作。
String x = map.get(name);
if(x == null) {
x = newString();
map.put(name, x);
}
returnx;
}
}