Hashtable和HashMap的总结

这是一个老生常谈的问题。

首先来看HashMap的实现:

HashMap是基于链表数组实现的,也就是说数组每个下标存放一个链表。结构图如下:


链表的每个元素为一个Entry<K,V>,这是一个存放键值对的专用结构,键值的存取最终都是由该结构完成的。

下面我们看一下HashMap存储的过程

当我们往HashMap中put元素的时候,先根据key的hashCode重新计算hash值,根据hash值得到这个元素在数组中的位置(即下标),如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。这里hashCode是利用Java JDK中的String 的hashCode()方法,而index下标的计算是通过hash值对数组长度取模得到的。这样的目的是为了能够均匀的而不是顺序的分布在数组bucket中。

取值过程与存储过程对应:

计算key值hash值,得到索引下标,查询数组元素,若是链表则利用key.equals(Object obj)来确定,不是链表直接返回value值。

这里插入一个知识点,就是在计算索引下标值的时候对于机器损耗是比较大的,这里Java利用了一个方法叫做indexFor(int h, int length)

static int indexFor(int h, int length) {
    return h & (length-1);
}
这个算法非常巧妙, 它通过  h & (table.length -1)  来得到该对象的保存位,而 HashMap 底层数组的长度总是  2  次方,这是 HashMap 在速度上的优化。在length是2的n次方时,h&(length-1)就等价于h%length,这在速度上的优化是很明显的。

HashMap的resize(rehash):

当HashMap中的元素越来越多的时候,hash冲突的几率也就越来越高,因为数组的长度是固定的。所以为了提高查询的效率,就要对HashMap的数组进行扩容,数组扩容这个操作也会出现在ArrayList中,这是一个常用的操作,而在HashMap数组扩容之后,最消耗性能的点就出现了:原数组中的数据必须重新计算其在新数组中的位置,并放进去,这就是resize

  那么HashMap什么时候进行扩容呢?当HashMap中的元素个数超过数组大小*loadFactor时,就会进行数组扩容,loadFactor的默认值为0.75,这是一个折中的取值。也就是说,默认情况下,数组大小为16,那么当HashMap中元素个数超过16*0.75=12(这个值就是代码中的threshold值,也叫做临界值)的时候,就把数组的大小扩展为 2*16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知HashMap中元素的个数,那么预设元素的个数能够有效的提高HashMap的性能。

HashMap 包含如下几个构造器:

  1.    HashMap():构建一个初始容量为 16,负载因子为 0.75 的 HashMap。
  2.    HashMap(int initialCapacity):构建一个初始容量为 initialCapacity,负载因子为 0.75 的 HashMap。
  3.    HashMap(int initialCapacity, float loadFactor):以指定初始容量、指定的负载因子创建一个 HashMap。
  4.    initialCapacity:HashMap的最大容量,即为底层数组的长度。
  5.    loadFactor:负载因子loadFactor定义为:散列表的实际元素数目(n)/ 散列表的容量(m)。
所以如果预知HashMap的元素数n,直接建立一个大于该n/0.75的HashMap会比直接建立一个HashMap()要节省资源。

注意:1. HashMap不是线程安全的,但可以使用Collections类的静态方法Collection.synchronizedMap(Map)返回一个线程安全的Map。

 2.  HashMap的键只能有一个是null,值可以有多个是null,所以利用get(key k)获得的值是null,并不能判断这个键对应的值是null还是不存在该键,实际上,我们不通过这个方法来判断键值的存在,而是用containsKey(key k) ,containsValue(Value v)来判断。


再来看Hashtable

Hashtable处处都和HashMap很像,无论是实现方法和存取方法,但有几点不同:

  1. Hashtable是线程安全的,可以直接在并发多线程中使用,HashMap在上面已经列出。
  2. Hashtable不允许key为null,存入null时会抛出异常。
  3. 不允许value为null,value存入null同样会抛异常。
由以上来看,Hashtable不如HashMap易用,常用中我们最长使用的也是HashMap,并且由于其线程非安全,所以在效率上也会高于Hashtable.

最后再提一点:

有时侯,我们可能想用一个hashtable来映射key的字符串到value的字符串。DOS、Windows和Unix中的环境字符串就有一些例子,如key的字符串PATH被映射到value的字符串C:\WINDOWS;C:\WINDOWS\SYSTEM。Hashtable是表示这些的一个简单的方法,但Java提供了另外一种方法。 
  Java.util.Properties类是Hashtable的一个子类,设计用于String keys和values。Properties对象的用法同Hashtable的用法相象,但是类增加了两个节省时间的方法,你应该知道。  
  Store()方法把一个Properties对象的内容以一种可读的形式保存到一个文件中。Load()方法正好相反,用来读取文件,并设定Properties对象来包含keys和values。  
  注意,因为Properties扩展了Hashtable,我们虽然可以用超类的put()方法来添加不是String对象的keys和values,但这是不可取的。另外,如果你将store()用于一个不包含String对象的Properties对象,store()将失败。作为put()和get()的替代, 我们应该用setProperty()和getProperty(),它们用String参数。














  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值