文档章节

HashMap Hashtable 的区别

钟声已经敲响
 钟声已经敲响
发布于 2016/10/21 13:13
字数 1819
阅读 39
收藏 1

Hashtable 和 HashMap 作为 Map 的基本特性

两者都实现了Map接口,基本特性相同

-          对同一个Key,只会有一个对应的value值存在

-          如何算是同一个Key? 首先,两个key对象的hash值相同,其次,key对象的equals方法返回真

 

内部数据结构

Hashtable和HashMap的内部数据结构相似

其基本内部数据结构是一个Entry数组 (transient Entry[] table)

-         数组元素为实现Map.Entry<K,V>接口的类,Hashtable和HashMap各自实现了自己的Entry类。

-         Entry包含一个Key-value对,以及一个next指针指向另一个Entry。多个Entry可以组成一个单向链表。

 

常用操作

数据插入操作: put(key,value)

-          根据Key的hash值计算出该Entry所应存放的位置(数组下标)

-          若该数组元素为空,直接放置Entry到此处

-          若多个不同的Key所计算得到的数组下标相同,新加入的Key-value对(Entry)会被加入到Entry单向链表中。Hashtable和HashMap都是将其插入链表首部.

-          若已经有相同的Key存在于这个链表中,则,新的value值会取代老的value

-          当Map中存放的Entry数量超过其限制( 数组长度 * 负荷因子)时,Map将自动重新调整数组大小并重新对Entry进行散列

数据查找:get(key)

-          根据Key的hash值计算出该Entry对所应存放的位置(数组下标)

-          得到该位置的第一个Entry对象,比较key和Entry.key,若hash值相同,并且equals为真,则该Entry是我们要找的Key-value对,否则继续沿next指针构成的单向链表查找

数据移除:remove(key)

-          按照上述数据查找的方式找到key所在的Entry对象,将其移除,并保持Entry单向链表的连通性

 

Hashtable 和 HashMap 的比较

 

Hashtable

HashMap

并发操作

使用同步机制,

实际应用程序中,仅仅是Hashtable本身的同步并不能保证程序在并发操作下的正确性,需要高层次的并发保护。

下面的代码试图在key所对应的value值等于x的情况下修改value为x+1

{

 value = hashTable.get(key);

   if(value.intValue()== x){

hashTable.put(key,      new Integer(value.intValue()+1));

   }

}

如2个线程同时执行以上代码,可能放入不是x+1,而是x+2.

没有同步机制,需要使用者自己进行并发访问控制

数据遍历的方式

Iterator 和 Enumeration

Iterator

是否支持fast-fail

用Iterator遍历,支持fast-fail

用Enumeration不支持fast-fail.

支持fast-fail

是否接受值为null的Key 或Value?

不接受

接受

根据hash值计算数组下标的算法

当数组长度较小,并且Key的hash值低位数值分散不均匀时,不同的hash值计算得到相同下标值的几率较高

 

hash = key.hashCode();

index=(hash&0x7FFFFFFF) % tab.length;

优于hashtable,通过对Key的hash做移位运算和位的与运算,使其能更广泛地分散到数组的不同位置

 

hash = hash (k);

index = indexFor(hash, table.length);

 

static int hash(Object x) {

 int h = x.hashCode();

h += ~(h << 9);

 h ^= (h >>> 14);

  h += (h << 4);

 h ^= (h >>> 10);

 return h;

}

static int indexFor(int h, int length) {

return h & (length-1);

}

 

Entry数组的长度

Ø         缺省初始长度为11,

Ø         初始化时可以指定initial capacity

Ø         缺省初始长度为16,

Ø         长度始终保持2的n次方

Ø         初始化时可以指定initial capacity,若不是2的次方,HashMap将选取第一个大于initial capacity 的2n次方值作为其初始长度

LoadFactor负荷因子

0.75

负荷超过(loadFactor * 数组长度)时,内部数据的调整方式

扩展数组:2*原数组长度+1

扩展数组: 原数组长度 * 2

两者都会重新根据Key的hash值计算其在数组中的新位置,重新放置。算法相似,时间、空间效率相同

       一般情况下,HashMap能够比Hashtable工作的更好、更快,主要得益于它的散列算法,以及没有同步。应用程序一般在更高的层面上实 现了保护机制,而不是依赖于这些底层数据结构的同步,因此,HashMap能够在大多应用中满足需要。推荐使用HashMap,如果需要同步,可以使用同 步工具类将其转换成支持同步的HashMap。

 

Map的效率

Map的效率与Entry数组大小及负荷因子的选取有密切关系。选取适当的数组大小有利于Key-value对的散列分布,并且,如果数组足够 大,将有效的减少重新调整数组的次数,提高效率。较小的负荷因子将占用更多的空间,但降低冲突的可能性,从而将加快访问和更新的速度。

另外,Key的hash值本身如果能保证较好的散列性,也有益于提高Map的读写效率。在effective java中,对hash()的重载有好的建议。

关于如何提高Map的执行效率,可参考《Java Map 集合类简介》http://www.oracle.com/technology/global/cn/pub/articles/maps1.html 。

 

辨析

 “Hashtable和HashMap的区别主要是前者是同步的,后者是快速失败机制保证不会出现多线程并发错误(Fast-Fail)。”,这是一个被很多文章转载过的概念,但其描述并不准确,容易引起误会。

实质上,Fast-fail与同步保护的是两种不同情况下的并发,两者不能拿来做比较。

Hashtable是同步的,在执行get,put,remove,size,clear等一次性读写操作时,使用了同步机制,避免了多个线程 同时读写Hashtable。但同步机制并不能避免在iterator或Enumeration遍历过程中其他线程对Hashtable的put、 remove、clear操作,这些写操作都会被毫无阻拦得成功执行。

快速失败机制主要目的在于使iterator遍历数组的线程能及时发现其他线程对Map的修改(如put、remove、clear等),因 此,fast-fail并不能保证所有情况下的多线程并发错误,只能保护iterator遍历过程中的iterator.next()与写并发.

其次,Hashtable的iterator遍历方式也是支持fast-fail的,不能说它没有快速失败机制。写一个简单的例程就可以证明这 一点,一个线程做iterator遍历,另一个线程向hashtable中put新的key和value,很容易就会观察到fast-fail 机制报告 ConcurrentModificationException.

 

补充一点小常识:

TreeMap的key是有顺序的,是自然顺序,也可以指定比较函数; 但默认不是按插入的顺序。 


为了让Map  JSON化后是按照插入顺序显示,可以使用LinkedHashMap吧。它内部有一个链表,保持插入的顺序。迭代的时候,也是按照插入顺序迭代,而且迭代比HashMap快。

 

ConcurrentMap  主要的子类是ConcurrentHashMap。

原理:一个ConcurrentHashMap 由多个segment 组成,每个segment 包含一个Entity 的数组。这里比HashMap 多了一个segment 类。该类继承了ReentrantLock 类,所以本身是一个锁。当多线程对ConcurrentHashMap 操作时,不是完全锁住map, 而是锁住相应的segment 。这样提高了并发效率。

 

构造函数的分析:

本文转载自:http://www.cnblogs.com/carbs/archive/2012/07/04/2576995.html

下一篇: synchronized详解
钟声已经敲响
粉丝 12
博文 36
码字总数 32502
作品 0
海淀
程序员
私信 提问
【JAVA学习】——hashmap和hashtable的区别

HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别。主要的区别有:线程安全性,同步(synchronization),以及速度。 1、HashMap几乎可以等价于Hashtable,除了...

fjj15732621696
2017/10/29
0
0
HashMap和Hashtable的区别

HashMap和Hashtable的比较是Java面试中的常见问题,用来考验程序员是否能够正确使用集合类以及是否可以随机应变使用多种思路解决问题。HashMap的工作原理、ArrayList与Vector的比较以及这个问...

LCZ777
2014/03/29
95
0
java HashMap与Hashtable区别

1.HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。 2.HashMap是非synchronized,而H...

随性_
2016/05/11
108
0
hashmap实现原理浅析

看了下JAVA里面有HashMap、Hashtable、HashSet三种hash集合的实现源码,这里总结下,理解错误的地方还望指正 HashMap和Hashtable的区别 HashSet和HashMap、Hashtable的区别 HashMap和Hashtab...

商者
2016/03/30
71
0
集合——HashMap和Hashtable的区别

HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别。主要的区别有:线程安全性,同步(synchronization),以及速度。 HashMap几乎可以等价于Hashtable,除了H...

亚特兰缇斯
2015/03/03
48
0

没有更多内容

加载失败,请刷新页面

加载更多

JS其他类型值转化为Boolean类型规则

本文转载于:专业的前端网站➤JS其他类型值转化为Boolean类型规则 由于最近在笔试的时候,发现好多关于其他类型转化为Boolean类型的题目,因此总结一下! 一、String类型转化为Boolean 1.转化...

前端老手
17分钟前
2
0
EurekaClient自动装配及启动流程解析

在上篇文章中,我们简单介绍了EurekaServer自动装配及启动流程解析,本篇文章则继续研究EurekaClient的相关代码 老规矩,先看spring.factories文件,其中引入了一个配置类EurekaDiscoveryClie...

Java学习录
23分钟前
3
0
析构函数是否必须为虚函数?为何?

在C++中,基类指针可以指向一个派生类的对象。如果基类的析构函数不是虚函数,当需要delete这个指向派生类的基类指针时,就只会调用基类的析构函数,而派生类的析构函数无法被调用。容易造成...

天王盖地虎626
23分钟前
2
0
【TencentOS tiny】深度源码分析(7)——事件

引言 大家在裸机编程中很可能经常用到flag这种变量,用来标志一下某个事件的发生,然后在循环中判断这些标志是否发生,如果是等待多个事件的话,还可能会if((xxx_flag)&&(xxx_flag))这样子做...

杰杰1号
27分钟前
2
0
聊聊nacos client的ServerHttpAgent

序 本文主要研究一下nacos client的ServerHttpAgent HttpAgent nacos-1.1.3/client/src/main/java/com/alibaba/nacos/client/config/http/HttpAgent.java public interface HttpAgent { ......

go4it
33分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部