1,hashmap
2、对应HashMap采用哈希表存储键值对元素的方式, 配合着上图做一些说明:
1)E*代表一个Node节点,每个Node节点就是我们理解的一个key-value的mapping映射。
2)每个Node除了保存了key和value的映射外呢,还保存了它下一Node的引用。例如图中,Eb保存了Ebb
的引用,而Ebb保存了Ebbb的引用。
3)HashMap的put(key,value)方法介绍
每一个链表,如Eb–>Ebb–>Ebbb,这三个节点的key是不相等的。那么你可能会问,为什么它们三个会被放
在一个链表中呢?是这样的。当你调用put(key,value)方法时,会根据key计算出一个hash值,然后再通
过这个hash值和map当前的长度计算出一个数值,然后将这个数值作为图中数组tab脚标而获取这个脚标
对应的Node节点。如果这个节点不存在,则直接创建一个新的Node节点,插入到数组中的你计算出的那个
脚标的位置。如果存在,则判断key和你put进来的key是否相等(注意相等的判定:hash值相等且equal
相等),如果相等,那么直接更新其值value,也就是我们常见的覆盖旧value操作,如果旧value为null,则
直接将null值设置为你传进来的value值,如果不相等(此处不介绍LinkedHashMap)则去以遍历的方式寻
找这个节点的next节点,如果和这个链表的每个节点的next节点都不相等,则在链表的最后一个Node节点
后创建新节点。如果其中判定有一个相等,那么进行覆盖值并返回旧值操作。当然,这只是put方法的概要
!
HashMap.put(key,value)方法的注意点:
(1)没有synchronized关键字修饰,意味着它是非线程安全的。
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
(2)key和value可以是null-null、null-value、key-null的形式。如果Map中不存在将要添加的元素那么返
回值为null,如果已经存在且value不为null,那么新value覆盖旧value,返回旧value,如果旧value为null,
那么将新value和key形成一个key-value映射,并返回null。key为null的时候,null也是一个键,键具有唯
一性,value可以重复。
3、HashMap的resize()方法理解
这个方法时HashMap容量不够时进行扩容的方法,觉得有必要说一下。
当HashMap的容量不够用时,再往容器中添加元素时,HashMap会进行扩容操作。当HashMap的容量为最大的
时,则不扩容,但是容器阈值会设置为Integer类型的最大值。当不是最大的时,容器会进行扩容,容量会变为
原来的二倍(此时也不大于最大容量),并且阈值也会随之变化,变为原阈值的二倍。扩容就是在堆中新
创建一个HashMap容器,然后将原来就的HashMap中的元素放到新容器中,放置的时候会重新计算每个Node节
点在哈希表中的位置。
4、HashMap的构造
我们常用的都是空参构造。空参构造一个HashMap,那么它的构造方法使用的是默认的初始化容量16和
加载因子0.75。
**
三、Hashtable
**
Hashtable已经被弃用的一个类,性能比较低,它有一些自己的特点,不知道你发现没有,它不符合大小驼
峰命名规则,这点很讨厌。
1、Hashtable的方法几乎都是同步的,都有synchronized关键字修饰,因此和HashMap相比,它是线程安全的。
public synchronized V put(K key, V value) {…}
2、Hashtable中key-value的映射,key和value 都是不允许为null的,如果为null了呢?对不起,空指针异常抛出。
3、Hashtable在计算节点元素在哈希表中的位置使用的算法稍有区别,它有它的好处,但和HashMap的算法比
起来明显性能低一些。
4、Hashtable的扩容是原来容量的二倍加1(2n+1),源码参考:int newCapacity = (oldCapacity << 1) + 1;
四**、HashMap和Hashtable有哪些主要区别呢?
**
1、HashMap是继承自AbstractMap类,而HashTable是继承自Dictionary类。不过它们都实现了同时实现了map、Cloneable(可复制)、Serializable(可序列化)这三个接口。
2、Hashtable比HashMap多提供了elments() 和contains() 两个方法。
3、HashMap的key-value支持key-value,null-null,key-null,null-value四种。而Hashtable只支持key-value一种(即
key和value都不为null这种形式)。既然HashMap支持带有null的形式,那么在HashMap中不能由get()方法来判断
HashMap中是否存在某个键, 而应该用containsKey()方法来判断,因为使用get的时候,当返回null时,你无法判断到底
是不存在这个key,还是这个key就是null,还是key存在但value是null。
4、线程安全性不同,HashMap的方法都没有使用synchronized关键字修饰,都是非线程安全的,而Hashtable的方法几乎
都是被synchronized关键字修饰的。但是,当我们需要HashMap是线程安全的时,怎么办呢?我们可以通过Collections.synchronizedMap(hashMap)来进行处理,亦或者我们使用线程安全的ConcurrentHashMap。ConcurrentHashMap虽然也是线程安全的,但是它的效率比Hashtable要高好多倍。因为ConcurrentHashMap使用了分段锁,并不对整个数据进行锁定。
5、初始容量大小和每次扩充容量大小的不同
Hashtable默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。
6、计算hash值的方法不同
为了得到元素的位置,首先需要根据元素的 KEY计算出一个hash值,然后再用这个hash值来计算得到最终的位置。
Hashtable直接使用对象的hashCode。hashCode是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值。然后再使用除留余数发来获得最终的位置。
Hashtable在计算元素的位置时需要进行一次除法运算,而除法运算是比较耗时的。
HashMap为了提高计算效率,将哈希表的大小固定为了2的幂,这样在取模预算时,不需要做除法,只需要做位运算。位运算比除法的效率要高很多。
HashMap的效率虽然提高了,但是hash冲突却也增加了。因为它得出的hash值的低位相同的概率比较高,而计算位运算
为了解决这个问题,HashMap重新根据hashcode计算hash值后,又对hash值做了一些运算来打散数据。使得取得的位置更加分散,从而减少了hash冲突。当然了,为了高效,HashMap只做了一些简单的位处理。从而不至于把使用2 的幂次方带来的效率提升给抵消掉。
作者:御风御吾
来源:CSDN
原文:https://blog.csdn.net/luojishan1/article/details/81952147
版权声明:本文为博主原创文章,转载请附上博文链接!