HashMap源码分析
来源:互联网 发布:淘宝店家联系方式 编辑:程序博客网 时间:2024/05/21 09:53
1.Hashmap中几个重要的参数(JDK 1.7)
(1)初始容量(initialCapacity) 16
(2)扩容极限 1<<30 也就是2的31次方
(3)容量必须是2的n次方,如果初始化时的参数不满足,会自动round-up
(4)散列函数 对于一个哈希表来说,散列函数决定了访问的效率的高低
2.Hashmap源码分析
2.1插入
ublic V put(K key, V value) { if (table == EMPTY_TABLE) { //如果表为空就初始化表 inflateTable(threshold); } if (key == null) //#细节1 如果key为null return putForNullKey(value); //key不为空时 //先计算key的hash值,JDK1.7的源码的hash函数比较繁琐,建议看1.8的 //细节2 int hash = hash(key); //计算出要放在table数组中的位置i int i = indexFor(hash, table.length); //如果在位置i的链中找到这个key就覆盖 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; //#细节3 e.recordAccess(this); return oldValue; } } // modCount++; addEntry(hash, key, value, i); return null;}
#细节1 关于null key的处理
private V putForNullKey(V value) { //在table数组头寻找key为null的Entry //为什么要在table[0]查找?因为在hash()方法中key为null的值的hash为0 for (Entry<K,V> e = table[0]; e != null; e = e.next) { //如果找到的话,就用新的value值去覆盖旧的value值,然后返回新的value值 if (e.key == null) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; //如果没找到的话,就新建一个Entry //void addEntry(int hash, K key, V value, int bucketIndex) addEntry(0, null, value, 0); return null;}
#细节2 hash()散列函数
建议看https://www.zhihu.com/question/20733617里的解析,讲得很好,是JDK1.8的
#细节3 recordAccess
/** * This method is invoked whenever the value in an entry is * overwritten by an invocation of put(k,v) for a key k that's already * in the HashMap. *///这个方法是为了重写来实现在插入值被覆盖时执行某些算法的,LinkedHashMap可以通过重写这个函数来实现LRU,同样的也有重写recordRemoval()来实现删除某个key时执行某些算法void recordAccess(HashMap<K,V> m) {}
2.2删除
public V remove(Object key) { Entry<K,V> e = removeEntryForKey(key); return (e == null ? null : e.value); }
remove的实现是通过removeEntryForKey来实现的
final Entry<K,V> removeEntryForKey(Object key) { //容量为0时返回null if (size == 0) { return null; } //计算这个key的hash值来定位这个key放在table数组中的位置 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; //如果根据hash值找到了key if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))//如果相比较的两个key的指向的物理地址相同或者逻辑含义相同,很细节{ modCount++; size--; if (prev == e) table[i] = next; else prev.next = next; e.recordRemoval(this); return e; } //如果没找到的话 prev = e; e = next; } //如果一直没找到,最后e会为null return e; }
2.3扩容
void transfer(Entry[] newTable, boolean rehash) { int newCapacity = newTable.length; //对每个key取hash值,然后通过indexFor方法来决定放在哪一位 //具体来说就是(以JDK1.8的散列函数为例) //假如原来的容量是16,indexFor时要看hash值最后4位,新的容量是32,indexFor 时要看hash值最后5位,如果多出来的这一位为0,就放在原先的桶里,如果多 出来的这一位为1,就放在oldIndex+oldCapacity里,也就是oldIndex+16 for (Entry<K,V> e : table) { while(null != e) { Entry<K,V> next = e.next; if (rehash) { e.hash = null == e.key ? 0 : hash(e.key); } int i = indexFor(e.hash, newCapacity); e.next = newTable[i]; newTable[i] = e; e = next; } }}
3.HashMap JDK1.8与JDK1.7的对比
(1)新增了红黑树,如果table[i]链的长度大于TREEIFY_THRESHOLD(默认为8),就会把这个链转化为一个TreeBin节点,里面是一个红黑树。如果扩容时新链的长度小于UNTREEIFY_THRESHOLD时(默认为6),就把它还原成一个链表。
(2)散列函数的效率上有提升,具体见https://www.zhihu.com/question/20733617的分析
0 0
- 源码分析:HashMap
- 源码分析:HashMap
- HashMap源码分析
- HashMap 源码分析
- HashMap源码分析
- HashMap LinkedHashMap源码分析
- HashMap源码分析
- HashMap 源码分析
- HashMap源码分析
- HashMap源码分析
- HashMap源码分析
- Java HashMap 源码分析
- HashMap源码分析
- java HashMap源码分析
- 源码分析HashMap
- HashMap源码分析
- HashMap源码分析
- HashMap源码分析
- Popupwindow里用EditText获取焦点弹出软键盘的问题
- 四款消息队列的比较
- 文本情感分析+python+正面和负面新闻+新浪微博+情感字典+机器学习
- 不得不看的Java代码性能优化总结
- Educational Codeforces Round 20 Maximal Binary Matrix
- HashMap源码分析
- 链表3:反转链表
- 初始ajax和跨域
- 树状数组 or 归并树 —— HDU 4417
- C# 人脸识别
- 跟小刀学 数据结构 双向链表
- RabbitMQ,ActiveMq,ZeroMq比较
- reactnative Navigator api解释
- HDU3709