java --HashMap和HashTable区别
2016-04-23 22:04
295 查看
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {}
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable {
public synchronized Enumeration<K> keys() {
return this.<K>getEnumeration(KEYS);
}
public synchronized Enumeration<V> elements() {
return this.<V>getEnumeration(VALUES);
}
}
Hashtable和
HashMap做为
Map的基本特性
两者都实现了Map接口,基本特性相同
- 对同一个Key,只会有一个对应的value值存在
- 如何算是同一个Key? 首先,两个key对象的hash值相同,其次,key对象的equals方法返回真
内部数据结构
Hashtable和HashMap的内部数据结构相似
其基本内部数据结构是一个Entry数组 (transientEntry[] 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 的比较
一般情况下,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的,不能说它没有快速失败机制。
implements Map<K,V>, Cloneable, Serializable {}
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable {
public synchronized Enumeration<K> keys() {
return this.<K>getEnumeration(KEYS);
}
public synchronized Enumeration<V> elements() {
return this.<V>getEnumeration(VALUES);
}
}
Hashtable和
HashMap做为
Map的基本特性
两者都实现了Map接口,基本特性相同
- 对同一个Key,只会有一个对应的value值存在
- 如何算是同一个Key? 首先,两个key对象的hash值相同,其次,key对象的equals方法返回真
内部数据结构
Hashtable和HashMap的内部数据结构相似
其基本内部数据结构是一个Entry数组 (transientEntry[] 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值计算其在数组中的新位置,重新放置。算法相似,时间、空间效率相同 |
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的,不能说它没有快速失败机制。
相关文章推荐
- spring如何下载源码和jar包
- 深入理解Spring--动手实现一个简单的SpringIOC容器
- JAVA基础(一)序列化,反序列化,java的序列化
- HashMap学习
- java开发命名规范
- 输出随机数字的认证图片
- 【结构型模式】桥接模式(Bridge)之23种java设计模式
- 关于spring的那点事
- 重新学javaweb---ServletContext
- 在Eclipse中怎样写Java注释
- 【Spring进阶】AOP主要概念
- spring中@param和mybatis中@param使用差别
- Jaxb annotation初步使用 javabean转xml
- java内存管理和分配
- 使用spring创建定时器
- JDK环境变量配置
- Java进阶(二十七)使用Dom4j解析XML文件
- Java进阶(二十七)使用Dom4j解析XML文件
- Java Thread join() 的用法
- 20145107 《Java程序设计》第八周学习总结