HashMap实现原理及安全性能
来源:互联网 发布:丽莎.蓝道尔 知乎 编辑:程序博客网 时间:2024/04/27 15:26
1.基本概念
HashMap是实现Map接口的非同步类,它是结合了数组和链表的特点做到了快速插入、删除(链表)、查找。
2. HashMap的数据结构:
在java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外。HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。
从上图中可以看出,HashMap底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组。
3. HashMap的存取实现:
1)存储
public V put(K key, V value) {
// HashMap允许存放null键和null值。
// 当key为null时,调用putForNullKey方法,将value放置在数组第一个位置。
if (key == null)
return putForNullKey(value);
// 根据key的keyCode重新计算hash值。
int hash = hash(key.hashCode());
// 搜索指定hash值在对应table中的索引。
int i = indexFor(hash, table.length);
// 如果 i 索引处的 Entry 不为 null,通过循环不断遍历 e 元素的下一个元素。
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
// 如果i索引处的Entry为null,表明此处还没有Entry。
modCount++;
// 将key、value添加到i索引处。
addEntry(hash, key, value, i);
return null;
}
当我们往HashMap中put元素的时候,先根据key的hashCode重新计算hash值,根据hash值得到这个元素在数组中的位置(即下标),如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。
当系统决定存储HashMap中的key-value对时,完全没有考虑Entry中的value,仅仅只是根据key来计算并决定每个Entry的存储位置。我们完全可以把 Map 集合中的 value 当成 key 的附属,当系统决定了 key 的存储位置之后,value 随之保存在那里即可。(HashMap中不允许存储key相同的两个元素)。
2)读取
从HashMap中get元素时,首先计算key的hashCode,找到数组中对应位置的某一元素,然后通过key的equals方法在对应位置的链表中找到需要的元素。
public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}
4 . HashMap的性能
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
这段代码保证初始化时HashMap的容量总是2的n次方,即底层数组的长度总是为2的n次方。当length总是 2 的n次方时,h& (length-1)运算等价于对length取模,也就是h%length,但是&比%具有更高的效率。当数组长度为2的n次幂的时候,不同的key算得得index相同的几率较小,那么数据在数组上分布就比较均匀,也就是说碰撞的几率小,相对的,查询的时候就不用遍历某个位置上的链表,这样查询效率也就较高了。
当HashMap中的元素越来越多的时候,hash冲突的几率也就越来越高,因为数组的长度是固定的。所以为了提高查询的效率,就要对HashMap的数组进行扩容,数组扩容这个操作也会出现在ArrayList中,这是一个常用的操作,而在HashMap数组扩容之后,最消耗性能的点就出现了:原数组中的数据必须重新计算其在新数组中的位置,并放进去,这就是resize。 HashMap的装填因子为0.75,当数组元素所占的大小超过此限制时就要对HashMap的数组实行resize.这样就可以扩大底层数组的大小,减小碰撞的几率,提高性能。
5. 线程安全性
HashMap不是线程安全的,往往在写程序时需要通过一些方法来回避.其实JDK原生的提供了2种方法让HashMap支持线程安全.
方法一:通过Collections.synchronizedMap()返回一个新的Map,这个新的map就是线程安全的. 这个要求大家习惯基于接口编程,因为返回的并不是HashMap,而是一个Map的实现.
方法二:重新改写了HashMap,具体的可以查看java.util.concurrent.ConcurrentHashMap. 这个方法比方法一有了很大的改进.
通过Collections.synchronizedMap()来封装所有不安全的HashMap的方法,就连toString, hashCode都进行了封装,而ConcurrentMap把HashMap进行了拆分,拆分成了多个独立的块,这样在高并发的情况下减少了锁冲突的可能.它实际上是锁住了每个桶,而hashTable是锁住了整个hash表,因此ConcurrentMap相对于hashTable具有更高的并发性。
- HashMap实现原理及安全性能
- Hashmap实现原理及使用
- HashMap实现原理及自定义
- HashMap的原理及实现
- HashMap的工作原理及性能分析
- HashMap实现原理和性能分析
- HashMap实现原理分析及简单实现一个HashMap
- HashMap实现原理分析及简单实现一个HashMap
- HashMap源代码实现及性能优化
- HashMap的实现原理及源码
- HashMap和HashSet原理及底层实现
- HashMap实现原理及源码分析
- Java HashMap 实现原理及数据结构
- Java HashMap工作原理及实现
- Java HashMap工作原理及实现
- Java HashMap工作原理及实现
- Java HashMap工作原理及实现
- Java HashMap工作原理及实现
- ldd命令的介绍
- static作用
- android 自动接电话
- 小甲鱼PE详解之输入表(导入表)详解2(PE详解08)
- pathForResource:imgPath
- HashMap实现原理及安全性能
- hdu 4788 Hard Disk Drive
- 第一次正儿八经写博客
- 表单美化-原生javascript和jQuery下拉列表(兼容IE6)
- 小甲鱼PE详解之输入表(导出表)详解(PE详解09)
- Linux shell中反引号(`)的应用
- XCode6.0的iOS免证书真机测试方法
- Activity管理
- 【绿网天下·教育篇】家庭教育的十个“不要”