HashMap是Java程序员使用频率最高的用于映射(键值对)处理的数据类型。
Java为数据结构中的映射定义了一个接口java.util.Map,此接口主要有四个常用的实现类分别是HashMap、Hashtable、LinkedHashMap和TreeMap,类继承关系如下图所示:
这几个实现类的区别与联系可以总结为以下几点:
1)HashMap:
非线程安全,是根据键的hashCode值来存储数据,具有很快的访问速度,但是遍历顺序不确定。 在JDK1.8之前HashMap的底层是由数组+链表实现的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突)。 JDK1.8以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间;
所谓 “拉链法” 就是:将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可。
2)Hashtable:
线程安全,作为遗留类,其常用功能和HashMap类似。由于任一时间只有一个线程能写Hashtable,并发性不如ConcurrentHashMap,因为ConcurrentHashMap引入了分段锁( JDK1.7的 ConcurrentHashMap 底层采用 分段的数组+链表 实现,JDK1.8 取消了Segment分段锁,采用CAS和synchronized来保证并发安全。数据结构跟HashMap1.8的结构类似,数组+链表/红黑二叉树。synchronized只锁定当前链表或红黑二叉树的首节点,这样只要hash不冲突,就不会产生并发,效率又提升N倍)。Hashtable的底层实现和JDK1.8之前HashMap一样,就是数组+链表,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的;但是,Hashtable中没有红黑树的机制。
3)LinkedHashMap:
是HashMap的一个子类,保存了记录的插入顺序。在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的,也可以在构造时带参数,按照访问次序排序。它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。
4)TreeMap:
实现的是SortedMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。底层实现是红黑树(自平衡的排序二叉树)
小结:Hashtable不建议在新代码中使用,不需要线程安全的场合可以用HashMap替换,需要线程安全的场合可以用ConcurrentHashMap替换。