容器--HashMap
一、前言
看了下上一篇博客已经是半个月前,将近20天前了,很惭愧没有坚持下来,这期间主要是受奥运会和王宝强事件的影响,另外加上HashMap中关于rehash的实现比较不好理解,所以就一拖再拖。如果能坚持,也许容器这一部分已经写完了。
不管怎么样,奥运总算是结束了,到年底来说,也没有太多可以关注的内容了,坚持下来,做总比不做好。
回到正题,本文要分析的是Map中的经典实现,HashMap. 顾名思义是通过Hash来查找和存储元素的Map, 这种Map定位方便,支持自动扩容,是一种效率比较高的非线程安全的Map。下面做详细的介绍。
二、存储介绍
HashMap的存储相对于之前我们介绍的List要复杂一些,它使用了数组和链表相结合的方式,一个示例结构如下:

上图大致描述了HashMap的存储结构,即其内部首先是通过对key进行hash, 根据其hash值映射到内部的数组中的某一个位置,但考虑到hash冲突的可能性,所以对于相同hash值而key不同的元素,再通过一个链表进行存储。
也就是说,实际上这个数组中存储的是链表的头元素,每个节点都是一个Entry类型的对象,其包括了hash值,key, value及指向下一个节点的指针。
三、实现分析
根据上一节的介绍,对于map来说,需要实现一些常用而重要的功能,如put,get,delete,search等,本节就来分析一下对于这些功能, hashMap是怎么实现的。
3.1 添加节点, put.
根据map的特点,其key是惟一的,而且允许key为null, 所以,在向map中添加一个元素,需要考虑这样几个问题。
1)key==null的情况下,其hash值如何计算?
如果key==null,这种情况下,直接将key映射到下标为0的位置
2)key已经存在的情况下,如何处理?
将key的value设置为当前值,原来的值直接返回。
3)以什么样的机制扩容?
HashMap内部有一个叫threshold的值,这个值被称之为阈值,初始时,是容量*负载因子,如果当前的map中的元素个数达到这个阈值,且当前映射的位置已经有元素的话,则会对内部的table进行扩容,将其扩大一倍,并对原来的元素重新映射。
这样做是很有必要的,否则当map中的元素过多的话,就容易造成大量的key映射之后的值是同一个下标,这样就影响了hash的存取效率。
4)如何查找key是否存在
如果key为null的话,直接定位到数组中下标为0的位置,否则要先计算hash值,计算完了之后再根据hash值映射到对应的下标。
找到下标后,要看该下标中如果没有链表,则创建一个,如果有的话,则需要依次遍历链表,比较节点的key是否和当前的key相同,比较的逻辑如下:
int hash = hash(key);
int i = indexFor(hash, table.length);//table中的位置
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k; //entry相同的条件 , hash相同 , key的引用相同,或者equals()
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
可以看到,首先要保证hash值一样,再判断key是否相同或equals
如果没有找到匹配的确实需要添加的话,HashMap采用了个非常巧妙的方式,将元素添加到链表中的头节点,这样省去了遍历。
3.2 查找
查找的逻辑其实和添加时对key的查找是类似的,没有找到的话,直接返回null.
HashMap中对于containsValue的实现比较简单,直接遍历数组的每个下标,再遍历每个下标中的节点,这是一个双重循环,效率相对比较低下,所以这个方法一般情况下不要使用。
3.3 删除
删除的查找过程和前面类似,找到后的删除逻辑如下:
final Entry<K,V> removeEntryForKey(Object key) {
if (size == 0) {
return null;
}
int hash = (key == null) ? 0 : hash(key);
int i = indexFor(hash, table.length);
Entry<K,V> prev = table[i];
Entry<K,V> e = prev;
while (e != null) {
Entry<K,V> next = e.next;
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
modCount++;
size--;
if (prev == e)
table[i] = next;
else
prev.next = next;
e.recordRemoval(this);
return e;
}
prev = e;
e = next;
}
return e;
}
这里主要是对于链表的修改,如果是头节点则指向下一个,否则指向头节点。
3.4 遍历
常见的是获取keySet()以及entrySet(),当然也可以获取值集合values(), 这在内部是通过迭代器来实现的。
如果我们要遍历整个map, 最理想的方式是获取entrySet(),这样直接取其key和value即可,而不是获取keySet。
四、总结
理解了HashMap的存储结构之后,那么对于其各种操作的实现也就不难理解了,hashMap中,如何生成hash值,以及是否需要rehash,这一部分是不太好理解的,个人也没理解得太透彻,所以本文没做介绍,有时间的话再做研究。
容器--HashMap的更多相关文章
- java容器HashMap原理
1.为什么需要HashMap 前面我们说了ArrayList和LinkedList,它们对容器内的对象都能实现增.删.改.查.遍历等操作, 并且对应不同的情况,我们可以选择不同的List,用以提高效率 ...
- Java修炼——容器HashMap用法
直接上代码,容器集合之间的关系在后面我会继续详细分析,这次先看HashMap用法 HashMap的方法都在代码中有解释.有需要的可以仔细看看 package com.bjsxt.map; import ...
- Map容器——HashMap及常用API,及put,get方法解析,哈希码的产生和使用
Map接口 ① 映射(map)是一个存储键/值对的对象.给定一个键,可以查询到它的值,键和值都是对象; ② 键必须是唯一的,值可以重复; ③ 有些映射可以接收null键和null值,而有的 ...
- 容器HashMap原理(学习)
一.概述 基于哈希表的 Map 接口的非同步实现,允许使用 null 值和 null 键,不保证映射的顺序 二.数据结构 HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体:Has ...
- 深入理解Java容器——HashMap
目录 存储结构 初始化 put resize 树化 get 为什么equals和hashCode要同时重写? 为何HashMap的数组长度一定是2的次幂? 线程安全 参考 存储结构 JDK1.8前是数 ...
- JAVA编程思想(第四版)学习笔记----11.4 容器的打印
import static java.lang.System.out; import java.util.ArrayList; import java.util.Collection; import ...
- Java基础学习总结--对象容器
目录: ArrayList 顺序泛型容器 HashSet 集合容器 HashMap<Key,Value>容器 要用Java实现记事本的功能.首先列出记事本所需功能: 可以添加记录(字符串) ...
- HashMap:从源码分析到面试题
1 HashMap简介 HashMap是实现map接口的一个重要实现类,在我们无论是日常还是面试,以及工作中都是一个经常用到角色.它的结构如下: 它的底层是用我们的哈希表和红黑树组成的.所以我们在学习 ...
- 【读书笔记《Android游戏编程之从零开始》】6.Android 游戏开发常用的系统控件(TabHost、ListView)
3.9 TabSpec与TabHost TabHost类官方文档地址:http://developer.android.com/reference/android/widget/TabHost.htm ...
随机推荐
- salesforce 零基础开发入门学习(十四)salesforce中工厂模式的运用
提到工厂模式,想必大家都很熟悉,工厂模式作为一种设计模式,同样在salesforce中适用. 举一个例子,笔作为基类,可以有钢笔,铅笔,圆珠笔等等.有一个笔的工厂,当你向它要钢笔,它就会生产一支钢笔; ...
- draggable属性设置元素是否可拖动。
设置标签属性draggable="true"将一个标签内的元素拖动到另外一个标签进行显示: <!DOCTYPE HTML> <html> <head& ...
- C# 围棋盘的画法
C#绘图不是那么美,不过对于简单的图形,不注重美感的图质,用C#还是很方便的. 背景颜色.绘制图表线色.纵横列大小可按照个人喜好调节. 不提供AI代码,我自己设计的AI不是很完美,就不拿出来献丑了,算 ...
- Mybatis中SqlMapper配置的扩展与应用(1)
奋斗了好几个晚上调试程序,写了好几篇博客,终于建立起了Mybatis配置的扩展机制.虽然扩展机制是重要的,然而如果没有真正实用的扩展功能,那也至少是不那么鼓舞人心的,这篇博客就来举几个扩展的例子. 这 ...
- Visual Studio Emulator for Android 里面的安卓模拟器如何启用
打开软件
- CSS学习目录
前面的话 CSS是前端工程师的基本功,但好多执迷于学习javascript的人的基本功并不扎实.可能一些人从w3school网站匆匆过了一遍,只是对CSS常用概念有一些表面上的理解,就一头扎进java ...
- Spring MVC 学习总结(二)——控制器定义与@RequestMapping详解
一.控制器定义 控制器提供访问应用程序的行为,通常通过服务接口定义或注解定义两种方法实现. 控制器解析用户的请求并将其转换为一个模型.在Spring MVC中一个控制器可以包含多个Action(动作. ...
- Spark入门实战系列--5.Hive(上)--Hive介绍及部署
[注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .Hive介绍 1.1 Hive介绍 月开源的一个数据仓库框架,提供了类似于SQL语法的HQ ...
- Lining.js - 为CSS提供 ::nth-Line 选择器功能
在CSS中,我们使用 ::first-line 选择器来给元素第一行内容应用样式.但目前还没有像 ::nth-line.::nth-last-line 甚至 ::last-line 这样的选择器.实际 ...
- ECharts+BaiduMap+HT for Web网络拓扑图应用
前一篇谈及到了ECharts整合HT for Web的网络拓扑图应用,后来在ECharts的Demo中看到了有关空气质量的相关报表应用,就想将百度地图.ECharts和HT for Web三者结合起来 ...