




已阅读5页,还剩6页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
HashMap的初始容量(initialCapacity)和装载因子(loadFactor)按HashMap源码里的那种重构方法,如果reHash过多,显然会影响性能。所以为了防止过多的reHash,我们需要自己配置HashMap的装载因子loadFactor和初始的table容量capacity的大小(可以在构造函数里配或者调用方法配)。很容易理解,如果我们已经知道我们使用的HashMap一般情况的存储在1W对以上,你给它一个默认的16的初始的table容量,默认reHash每次容量翻倍,这得重构多少次呀!(如果装载因子为1,还得要约56次)。但是如果我们的对HashMap的容量需求不是很大,你给它一个默认1W的容量,显然又浪费宝贵的空间了。至于这两个参数的选择可以自己去把握,甚至可以设定动态绑定:分析历史数据,找出规律,或者预测未来的走向找出规律。对HashMap这两个参数实现一个动态的调整。比如早上8点9点A业务比较忙,它对应的HashMap可以提前多给些空间,而10点以后B业务使用的HashMap比较忙,A相对清闲,可以缩减A的空间给B。如果从数据库的表中读取记录存入HashMap中,完全可以根据记录的行数(row size)来初始化HashMap的容量,这样就可以达到reHash的最少次数,同时也保证了HashMap所需的最小容量:比如:通过SQL语句: select count(字段) as rowSize from 表提到行数:rowSize = 30那么我们在定义HashMap的时候:HashMap h = new HashMap(rowSize,1f);下面是HashMap的相关源代码部分:_/HashMap的构造public HashMap(int initialCapacity, float loadFactor) if (initialCapacity MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor = initialCapacityint capacity = 1; while (capacity initialCapacity) capacity = 1; this.loadFactor = loadFactor;threshold = (int)(capacity * loadFactor); table = new Entrycapacity; init(); public Vput(K key, V value) if (key = null) return putForNullKey(value); int hash = hash(key.hashCode(); int i = indexFor(hash, table.length); for (Entry e = tablei; 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; modCount+;addEntry(hash, key, value, i); return null; /添加新的项目void addEntry(int hash, K key, V value, int bucketIndex) Entry e = tablebucketIndex; tablebucketIndex = new Entry(hash, key, value, e); if (size+ = threshold) /注意理解这里,当前的实际大小(size)与threshold相等时,就将当前的容量扩大一倍 resize(2 * table.length); /重构大小void resize(int newCapacity) Entry oldTable = table; int oldCapacity = oldTable.length; if (oldCapacity = MAXIMUM_CAPACITY) threshold = Integer.MAX_VALUE; return; Entry newTable = new EntrynewCapacity; transfer(newTable); table = newTable; threshold = (int)(newCapacity * loadFactor); HashMap的数据结构 HashMap主要是用数组来存储数据的,我们都知道它会对key进行哈希运算,哈系运算会有重复的哈希值,对于哈希值的冲突,HashMap采用链表来解决的。在HashMap里有这样的一句属性声明:transient Entry table;Entry就是HashMap存储数据所用的类,它拥有的属性如下final K key;V value;final int hash;Entry next;看到next了吗?next就是为了哈希冲突而存在的。比如通过哈希运算,一个新元素应该在数组的第10个位置,但是第10个位置已经有Entry,那么好吧,将新加的元素也放到第10个位置,将第10个位置的原有Entry赋值给当前新加的 Entry的next属性。数组存储的是链表,链表是为了解决哈希冲突的,这一点要注意。几个关键的属性存储数据的数组transient Entry table;这个上面已经讲到了默认容量static final int DEFAULT_INITIAL_CAPACITY = 16;最大容量static final int MAXIMUM_CAPACITY = 1 =容量*加载因子时,HashMap会将容量扩容static final float DEFAULT_LOAD_FACTOR = 0.75f;当实际数据大小超过threshold时,HashMap会将容量扩容,threshold容量*加载因子int threshold;加载因子final float loadFactor;HashMap的初始过程构造函数1public HashMap(int initialCapacity, float loadFactor) if (initialCapacity MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor = initialCapacity int capacity = 1; while (capacity initialCapacity) capacity = 1; this.loadFactor = loadFactor; threshold = (int)(capacity * loadFactor); table = new Entrycapacity; init(); 重点注意这里while (capacity initialCapacity) capacity = 1;capacity才是初始容量,而不是initialCapacity,这个要特别注意,如果执行new HashMap(9,0.75);那么HashMap的初始容量是16,而不是9,想想为什么吧。构造函数2public HashMap(int initialCapacity) this(initialCapacity, DEFAULT_LOAD_FACTOR); 构造函数3,全部都是默认值 public HashMap() this.loadFactor = DEFAULT_LOAD_FACTOR; threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); table = new EntryDEFAULT_INITIAL_CAPACITY; init(); 构造函数4 public HashMap(Map m) this(Math.max(int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR); putAllForCreate(m); 如何哈希 HashMap并不是直接将对象的hashcode作为哈希值的,而是要把key的hashcode作一些运算以得到最终的哈希值,并且得到的哈希值也不是在数组中的位置哦,无论是get还是put还是别的方法,计算哈希值都是这一句:int hash = hash(key.hashCode();hash函数如下:static int hash(int h) return useNewHash ? newHash(h) : oldHash(h); useNewHash声明如下: private static final boolean useNewHash; static useNewHash = false; 这说明useNewHash其实一直为false且不可改变的,hash函数里对 useNewHash的判断真是多余的。private static int oldHash(int h) h += (h 14); h += (h 10); return h; private static int newHash(int h) / This function ensures that hashCodes that differ only by / constant multiples at each bit position have a bounded / number of collisions (approximately 8 at default load factor). h = (h 20) (h 12); return h (h 7) (h 4); 其实HashMap的哈希函数会一直都是oldHash。如果确定数据的位置看下面两行int hash = hash(k.hashCode(); int i = indexFor(hash, table.length);第一行,上面讲过了,是得到哈希值,第二行,则是根据哈希指计算元素在数组中的位置了,位置的计算是将哈希值和数组长度按位与运算。static int indexFor(int h, int length) return h & (length-1); put方法到底作了什么?public V put(K key, V value) if (key = null) return putForNullKey(value); int hash = hash(key.hashCode(); int i = indexFor(hash, table.length); for (Entry e = tablei; 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; modCount+; addEntry(hash, key, value, i); return null; 如果key为NULL,则是单独处理的,看看putForNullKey方法: private V putForNullKey(V value) int hash = hash(NULL_KEY.hashCode(); int i = indexFor(hash, table.length); for (Entry e = tablei; e != null; e = e.next) if (e.key = NULL_KEY) V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; modCount+; addEntry(hash, (K) NULL_KEY, value, i); return null; NULL_KEY的声明:static final Object NULL_KEY = new Object();这一段代码是处理哈希冲突的,就是说,在数组某个位置的对象可能并不是唯一的,它是一个链表结构,根据哈希值找到链表后,还要对链表遍历,找出key相等的对象,替换它,并且返回旧的值。 for (Entry e = tablei; e != null; e = e.next) if (e.key = NULL_KEY) V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; 如果遍历完了该位置的链表都没有找到有key相等的,那么将当前对象增加到链表里面去 modCount+; addEntry(hash, (K) NULL_KEY, value, i); return null;且看看addEntry方法 void addEntry(int hash, K key, V value, int bucketIndex) Entry e = tablebucketIndex;tablebucketIndex = new Entry(hash, key, value, e); if (size+ = threshold) resize(2 * table.length); tablebucketIndex = new Entry(hash, key, value, e);新建一个Entry对象,并放在当前位置的Entry链表的头部,看看下面的 Entry构造函数就知道了,注意红色部分。 Entry(int h, K k, V v, Entry n) value = v; next = n; key = k; hash = h; 如何扩容? 当put一个元素时,如果达到了容量限制,HashMap就会扩容,新的容量永远是原来的2倍。上面的put方法里有这样的一段:if (size+ = threshold) resize(2 * table.length);这是扩容判断,要注意,并不是数据尺寸达到HashMap的最大容量时才扩容,而是达到 threshold指定的值时就开始扩容, threshold最大容量加载因子。 看看resize方法 void resize(int newCapacity) Entry oldTable = table; int oldCapacity = oldTable.length; if (oldCapacity = MAXIMUM_CAPACITY) threshold = Integer.MAX_VALUE; return; Entry newTable = new EntrynewCapacity;transfer(newTable); table = newTable; threshold = (int)(newCapacity * loadFactor); 重点看看红色部分的 transfer方法void transfer(Entry newTable) Entry src = table; int newCapacity = newTable.length; for (int j = 0; j src.length; j+) Entry e = srcj; if (e != null) srcj = null; do Entry next = e.next; int i = indexFor(e.hash, newCapacity); e.next = newTablei; newTablei = e; e = next; while (e != null)
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 植物介绍课件
- 棕熊课件介绍
- 大型活动危货运输安全保障计划
- 钢结构机场航站楼安全管理措施
- 科技教育后进生成长计划
- 六年级数学课堂管理计划探讨
- 初三数学中考复习课程安排
- 2025年人教版二年级下册教师培训计划
- 机关单位午餐配送计划
- 企业消防演习总结及整改措施
- 保卫干事事迹材料
- GB/T 6913-2023锅炉用水和冷却水分析方法磷酸盐的测定
- 超星尔雅-《知识论导论》答案
- 精神科药物的合理使用演示
- 矿井巷道断面图册
- 热风炉安装使用说明书
- 集团公司全员安全生产职责清单(含目录)
- 8.6《林黛玉进贾府》课本剧剧本
- 分布式光伏发电项目安装验收表
- 足浴店消防安全的应急预案范文
- GB/T 21835-2008焊接钢管尺寸及单位长度重量
评论
0/150
提交评论