第五章 HashMap源码解析
2016-01-06 16:47
423 查看
5.1、对于HashMap需要掌握以下几点
Map的创建:HashMap()
往Map中添加键值对:即put(Object key, Object value)方法
获取Map中的单个对象:即get(Object key)方法
删除Map中的对象:即remove(Object key)方法
判断对象是否存在于Map中:containsKey(Object key)
遍历Map中的对象:即keySet(),在实际中更常用的是增强型的for循环去做遍历
Map中对象的排序:主要取决于所采取的排序算法
5.2、构建HashMap
源代码:
一些属性:
注意:
map中存放的key-value对个数size,该个数决定了数组的扩容(size>=threshold时,扩容),而非table中的所占用的桶的个数来决定是否扩容
标志位modCount采用volatile实现该变量的线程可见性(之后会在"Java并发"章节中去讲)
数组中的桶,指的就是table[i]
无参构造器(也是当下最常用的构造器)
注意:
init()为空方法
对于hashmap而言,还有两个比较常用的构造器,一个双参,一个单参。
View Code
总结:
HashMap底层就是一个Entry数组,Entry又包含next,事实上,可以看成是一个"链表数组"
扩容:map中存放的key-value对个数size,该个数决定了数组的扩容(size>=threshold时,扩容),而非table中的所占用的桶的个数来决定是否扩容
扩容过程,不会重新计算hash值,只会重新按位与
在实际使用中,若我们能预判所要存储的元素的多少,最好使用上述的单参构造器来指定初始容量
HashMap可以插入null的key和value
remove(Object key):若删除的key不存在于map中,返回null,不会抛异常。
HashMap线程不安全,若想要线程安全,最好使用ConcurrentHashMap
疑问:
在我们实际使用hashmap时,最好的情况是将key的hash值打散,使插入的这些Entry尽量落在不同的桶上[b](这样做的主要目的是提高查询效率),以下这个hash函数应该就是实现了这样的功能,但是为什么这样的hash函数可以将hash值打散,求大神指点!!![/b]
Map的创建:HashMap()
往Map中添加键值对:即put(Object key, Object value)方法
获取Map中的单个对象:即get(Object key)方法
删除Map中的对象:即remove(Object key)方法
判断对象是否存在于Map中:containsKey(Object key)
遍历Map中的对象:即keySet(),在实际中更常用的是增强型的for循环去做遍历
Map中对象的排序:主要取决于所采取的排序算法
5.2、构建HashMap
源代码:
一些属性:
static final int DEFAULT_INITIAL_CAPACITY = 16; // 默认的初始化容量(必须是2的多少次方) static final int MAXIMUM_CAPACITY = 1 << 30; // 最大指定容量为2的30次方 static final float DEFAULT_LOAD_FACTOR = 0.75f; // 默认的加载因子(用于resize) transient Entry[] table;// Entry数组(数组容量必须是2的多少次方,若有必要会扩容resize)--这就是HashMap的底层数据结构 transient int size; // 该map中存放的key-value对个数,该个数决定了数组的扩容(而非table中的所占用的桶的个数来决定是否扩容) // 扩容resize的条件:eg.capacity=16,load_factor=0.75,threshold=capacity*load_factor=12,即当该map中存放的key-value对个数size>=12时,就resize) int threshold; final float loadFactor; // 负载因子(用于resize) transient volatile int modCount;// 标志位,用于标识并发问题
注意:
map中存放的key-value对个数size,该个数决定了数组的扩容(size>=threshold时,扩容),而非table中的所占用的桶的个数来决定是否扩容
标志位modCount采用volatile实现该变量的线程可见性(之后会在"Java并发"章节中去讲)
数组中的桶,指的就是table[i]
无参构造器(也是当下最常用的构造器)
/** * 初始化一个负载因子、resize条件和Entry数组 */ public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR;// 负载因子:0.75 threshold = (int) (DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);//当该map中存放的key-value对个数size>=12时,就resize table = new Entry[DEFAULT_INITIAL_CAPACITY];// 设置Entry数组容量为16 init(); }
注意:
init()为空方法
对于hashmap而言,还有两个比较常用的构造器,一个双参,一个单参。
final Entry<K, V> nextEntry() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); Entry<K, V> e = next; if (e == null) throw new NoSuchElementException(); if ((next = e.next) == null) { Entry[] t = table; while (index < t.length && (next = t[index++]) == null) ; } current = e; return e; }
View Code
总结:
HashMap底层就是一个Entry数组,Entry又包含next,事实上,可以看成是一个"链表数组"
扩容:map中存放的key-value对个数size,该个数决定了数组的扩容(size>=threshold时,扩容),而非table中的所占用的桶的个数来决定是否扩容
扩容过程,不会重新计算hash值,只会重新按位与
在实际使用中,若我们能预判所要存储的元素的多少,最好使用上述的单参构造器来指定初始容量
HashMap可以插入null的key和value
remove(Object key):若删除的key不存在于map中,返回null,不会抛异常。
HashMap线程不安全,若想要线程安全,最好使用ConcurrentHashMap
疑问:
在我们实际使用hashmap时,最好的情况是将key的hash值打散,使插入的这些Entry尽量落在不同的桶上[b](这样做的主要目的是提高查询效率),以下这个hash函数应该就是实现了这样的功能,但是为什么这样的hash函数可以将hash值打散,求大神指点!!![/b]
static int hash(int h) { //这样的hash函数应该可以尽量将hash值打散 h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); }
相关文章推荐
- lesson2: Python:for语句简单应用
- UILabel 的使用,属性详解
- codevs 1155 金明的预算方案
- 关于普通端口映射的原理
- 关于VS中LNK1120与errorLNK2019问题
- Maven介绍与使用
- [bug] Field 'id' doesn't have a default value
- 3.1.4_Lists(列表)
- UITableView - 重用机制
- unsafe的使用报错
- mysql DECLARE CONTINUE HANDLER FOR NOT FOUND
- 【BZOJ1014】【JSOI2008】火星人prefix Splay处理区间,hash+dichotomy(二分)check出解
- CS0433: 类型“BasePage”同一时候存在于“c:\Windows\Microsoft.NETxxxxxxxxxxxxxxxx
- play(1) 第一个简单的应用
- 伪静态-URLRewrite 详解
- 7.配置zabbix报警
- 无线与PC的区别
- CentOS开机启动脚本的顺序
- 一个半圆形拖动条
- java中 裁判打分 得出平均分,最高分和最低分