Java集合类之HashMap源码分析
来源:互联网 发布:procedure执行sql语句 编辑:程序博客网 时间:2024/05/17 03:34
hash表是一种常见的数据结构,主要是通过hash算法将数据尽可能的散列开来存放,当要查找某一数据时,可以通过hash算法直接定位,节省了对比查找的时间。map是一种key、value形式的键值对,将hash表和map结合即形成了HashMap。
在Java中HashMap的数据是以Entry数组的形式存放的,HashMap通过对key进行hash运算得到一个数组下标,然后将数据存放到Entry数组对应的位置,又因为不同的key进行hash运算可能会得到一个相同的数组下标,为了解决碰撞覆盖冲突,所以Entry本身又是一个链表的结构,即以后不同的key相同数组下标的数据的next会被赋值为已存在Entry链表,新的Entry会替换数组值。
HashMap的存储数据的示例图如下:
HashMap 的put方法的源码解析
01
public
V put(K key, V value) {
02
if
(key ==
null
)
03
return
putForNullKey(value);
// HashMap接收key为null的数据
04
int
hash = hash(key.hashCode());
//对key的hashCode再进行hash运算
05
int
i = indexFor(hash, table.length);
//根据hash值和entry数组的大小计算出新增数据应该存放的数组位置
06
for
(Entry<k,v> e = table[i]; e !=
null
; e = e.next) {
07
// for循环遍历找到的数组下标的entry,如果hash值和key都相等,则覆盖原来的value值
08
Object k;
09
if
(e.hash == hash && ((k = e.key) == key || key.equals(k))) {
10
V oldValue = e.value;
11
e.value = value;
12
e.recordAccess(
this
);
13
return
oldValue;
14
}
15
}
16
17
modCount++;
18
//如果上面for循环没有找到相同的hash和key,则增加一个entry
19
addEntry(hash, key, value, i);
20
return
null
;
21
}</k,v>
1
void
addEntry(
int
hash, K key, V value,
int
bucketIndex) {
2
Entry<k,v> e = table[bucketIndex];
//找到下标的entry
3
//new 一个新的entry,赋值给当前下标数组
4
table[bucketIndex] =
new
Entry<k,v>(hash, key, value, e);
5
if
(size++ >= threshold)
6
resize(
2
* table.length);
7
}</k,v></k,v>
1
Entry(
int
h, K k, V v, Entry<k,v> n) {
2
value = v;
3
next = n;
//即将原来数组下标对应的entry赋值给新的entry的next
4
key = k;
5
hash = h;
6
}</k,v>
(1)hash值相同且key相等数据将被覆盖。
(2)添加新的entry时,将已存在的数据下标的entry(可能是null)赋值给新entry的next,新entry将替换原数组下标的值。
HashMap的get方法源码解析
01
public
V get(Object key) {
02
//key为null时特别处理
03
if
(key ==
null
)
04
return
getForNullKey();
05
int
hash = hash(key.hashCode());
06
//indexFor(hash, table.length) 根据hash值和数组长度计算出下标,然后遍历Entry链表
07
for
(Entry<k,v> e = table[indexFor(hash, table.length)];
08
e !=
null
;
09
e = e.next) {
10
Object k;
11
if
(e.hash == hash && ((k = e.key) == key || key.equals(k)))
12
return
e.value;
13
}
14
return
null
;
15
}</k,v>
总结
- 一个对象当HashMap的key时,必须覆盖hashCode()和equals()方法,hashCode()的返回值尽可能的分散。
- 当HashMap的entry的数组足够大,key的hash值足够分散时,即是可以实现一个entry数组下标最多只对应了一个entry,此时get方法的时间复杂度可以达到O(1)。
- 在数组长度和get方法的速度上要达到一个平衡。数组比较长碰撞出现的概率就比较小,所以get方法获取值时就比较快,但浪费了比较多的空间;当数组长度没有冗余时,碰撞出现的概率比较大,虽然节省了空间,但会牺牲get方法的时间。
- HashMap有默认的装载因子loadFactor=0.75,默认的entry数组的长度为16。装载因子的意义在于使得entry数组有冗余,默认即允许25%的冗余,当HashMap的数据的个数超过12(16*0.75)时即会对entry数组进行第一次扩容,后面的再次扩容依次类推。
- HashMap每次扩容一倍,resize时会将已存在的值从新进行数组下标的计算,这个是比较浪费时间的。在平时使用中,如果能估计出大概的HashMap的容量,可以合理的设置装载因子loadFactor和entry数组初始长度即可以避免resize操作,提高put的效率。
- HashMap不是线程安全的,多线程环境下可以使用Hashtable或ConcurrentHashMap。
- Java集合类之HashMap源码分析
- Java集合之HashMap源码实现分析
- Java集合系列之HashMap源码分析
- Java集合之HashMap源码实现分析
- Java集合之HashMap源码分析
- Java集合之HashMap源码分析
- java集合类源码分析一:HashMap
- 【Java集合类源码分析】HashMap源码分析一
- 【Java集合类源码分析】HashMap源码分析二
- java核心之集合框架——HashMap源码分析
- Java集合框架之Map---HashMap和LinkedHashMap源码分析
- java源码分析之集合框架HashMap 10
- Java集合框架之Map---HashMap和LinkedHashMap源码分析
- 【JAVA基础】集合类源码分析_HashMap/HashSet
- 从源码分析java集合【HashMap】
- Java集合源码分析→HashMap
- java源码分析之HashMap
- Java源码分析之HashMap
- 。。。。。。。。。
- 两种I/O多路复用模式:Reactor和Proactor
- Android API Guides——User Interface
- 时间是自己的
- 飞秋教程 2013 阳阳整理完整版
- Java集合类之HashMap源码分析
- UVA 712 (13.08.23)
- 异步FIFO最小深度计算方法及原理分析
- Android高手进阶教程(九)之----Android Handler的使用!!! http://weizhulin.blog.51cto.com/1556324/323922
- wince自启动
- LeetCode - Construct Binary Tree from Preorder and Inorder Traversal
- 从一位数组中提取最小k个元素
- LeetCode - Construct Binary Tree from Inorder and Postorder Traversal
- Banhui Again