date: 2017-03-26 19:55:33
先上两张图,比较清晰地解释了java中常用集合之间的关系。
HashMap与Hashtable的区别
1.HashMap不是同步的,线程不安全。hashtable则是同步的,线程安全,但不建议在新代码中使用,在不需要线程安全的情况下,可以用HashMap代替,在需要线程安全的情况,可以使用ConcurrentHashMap替代。
2.HashMap允许键值为空,HashTable不允许。
3.方法上,HashMap去掉了Hashtable的contains方法。
4.Hashtable是基于陈旧的Dictionary的Map接口的实现,而HashMap是基于哈希表的Map接口的实现
5.HashMap的iterator迭代器执行快速失败机制,也就是说在迭代过程中修改集合结构,除非调用迭代器自身的remove方法,否则以其他任何方式的修改都将抛出并发修改异常。而Hashtable返回的Enumeration不是快速失败的。
认识HashMap
HashMap的存储结构
HashMap是用数组+链表+红黑树(jdk1.8)来实现的。
哈希表为解决冲突,可以采用开放地址法和链地址法等来解决问题,Java中HashMap采用了链地址法。hash表的每个元素又分别链接着一个单链表,元素为头结点,如果不同的key映射到了相同的下标,那么就使用头插法,插入到该元素对应的链表。
HashMap的负载因子
首先,HashMap的初始化长度length(默认值是16),Load factor为负载因子(默认值是0.75)。
负载因子=哈希表中的元素/哈希表的长度
默认的负载因子0.75是对空间和时间效率的一个平衡选择
哈希表长度固定的情况下
当负载因子增大,空间的利用率增大,Hash冲突的概率增加,在进行get,put操作时,消耗更多时间,节约内存;
当负载因子减小,空间的利用率减小,Hash冲突的概率降低,读取性能更好,但会占用更多内存;
能否让HashMap实现线程安全,如何做?
1、直接使用Hashtable,但是当一个线程访问HashTable的同步方法时,其他线程如果也要访问同步方法,会被阻塞住。举个例子,当一个线程使用put方法时,另一个线程不但不可以使用put方法,连get方法都不可以,效率很低,现在基本不会选择它了。
2、HashMap可以通过下面的语句进行同步:
Collections.synchronizeMap(hashMap);
3、直接使用JDK 5 之后的 ConcurrentHashMap,如果使用Java 5或以上的话,请使用ConcurrentHashMap。
JDK1.8中的HashMap
当HashMap因Hash冲突导致链表过长时,HashMap会动态的将它替换成一个红黑树,这话的话会将时间复杂度从O(n)降为O(logn),很大程度上提高了HashMap的性能。
认识HashTable
Hashtable继承于Dictionary类,实现了Map接口。Dictionary是声明了操作"键值对"函数接口的抽象类。 有一点注意,HashTable除了线程安全之外(其实是直接在方法上增加了synchronized关键字,比较古老,落后,低效的同步方式),还有就是它的key、value都不为null。另外Hashtable 也有 初始容量 和 加载因子。
默认加载因子也是 0.75,HashTable在不指定容量的情况下的默认容量为11。
参考文章:
美团:Java 8系列之重新认识HashMap
极乐科技Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结
官方hashtable API
官方HashMap api