HashMap、HashTable、ConcurrentHash
疑问
在Java集合框架中,其中有一个大类就是map,其中HashMap
算是最常用的map实现之一,而大家讨论HashMap的时候通常会和HashTable
、ConcurrentHashMap
一起比较,那么它们名字都带有hash
字眼,而在实现与实际应用中有什么区别和注意事项,下面我们来讨论(这里都是基于JDK7的实现下进行讨论的)。
简介
HashMap
基于哈希表的 Map 接口的实现,此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。
除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
HashTable
此类实现一个哈希表,该哈希表将键映射到相应的值。任何非null 对象都可以用作键或值。
为了成功地在哈希表中存储和获取对象,用作键的对象必须实现 hashCode 方法和 equals 方法。
ConcurrentHashMap
支持get
的完全并发和put
的所期望可调整并发的哈希表。此类遵守与Hashtable相同的功能规范,并且包括对应于 Hashtable的每个版本的方法。
不过,尽管所有操作都是线程安全的,但不意味着get
操作会锁定,并且不支持锁定整个表以某种防止所有访问的方式。此类可以通过程序完全与Hashtable进行互操作,这取决于其线程安全,而与其同步细节无关。
区别
主要区别
集合 | key允许为null
|
value允许为null
|
线程安全 | 同步机制 |
---|---|---|---|---|
HashMap | 是 | 是 | 否 | 无 |
HashTable | 否 | 否 | 是 | synchronized |
ConcurrentHashMap | 否 | 否 | 是 |
lock ,java8之后改为synchronized
|
HashMap与HashTable
HashMap与HashTable在使用和实现上基本没有什么却别,其中主要却别是:
- HashMap非线程安全,HashTable是线程安全的
- HashTable为了保证线程安全使用
synchronized
实现,必然也影响性能 - HashTable不允许
null
作为key和value,而HashMap允许 - HashMap默认初始化大小为16,HashTable为11;HashMap扩容时是
扩大两倍
,而HashTable扩容是两倍加一
还有一点就是,HashTable继承自java.util.Dictionary
类,有相关字典类的操作,例如keys
、elements
等接口。
HashTable与ConcurrentHashMap
HashTable虽然是线程安全,但是是通过synchronized
机制做了简单的实现,无论搜索
和操作
的时候都是对整理哈希表锁定,比较影响性能。
ConcurrentHashMap的提出就是为了解决这个问题。不同于HashTable,ConcurrentHashMap是通过Segment
进行分隔从而只是锁住哈希表的一部分,提高性能,这也是两者的主要区别。
HashMap和ConcurrentHashMap
对于HashMap和ConcurrentHashMap在API上基本没什么却别,只不过在实现和使用上有点区别:
- 数据结构不同,ConcurrentHashMap在HashMap基础通过
Segment
进行分隔 - 线程安全,HashMap非线程安全,而ConcurrentHashMap是线程安全的,但不代表多线程访问的时候对于通用的key拿到同样的值
总结
从上面可以看出,对于从Java1.0版本就提出来的HashTable其实已经过时(并不是deprecated,只是使用上过时),因为对于普通的Map使用场景使用HashMap就足够了,而对于多线程并发的场景下ConcurrentHashMap表现出更好的性能,所以日常的使用HashMap和ConcurrentHashMap就足够了,而HashTable的存在可能就是Java平台保持兼容性而一直留下来的吧。