jdk 源码分析(1)java hashmap的结构
来源:互联网 发布:cg织梦 编辑:程序博客网 时间:2024/06/05 02:29
最近有人问我hashmap的底层是什么,冥冥中有点记忆,但是确实是忘了,所以今天把jdk的代码看来一下,后面几天将补几篇博客。
1)首先数据的存储定义了最小单元Node
static class Node<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Node<K,V> next; Node(int hash, K key, V value, Node<K,V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; }
自定next ,hash, key和value
2)树结构:
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> { TreeNode<K,V> parent; // red-black tree links TreeNode<K,V> left; TreeNode<K,V> right; TreeNode<K,V> prev; // needed to unlink next upon deletion boolean red; TreeNode(int hash, K key, V val, Node<K,V> next) { super(hash, key, val, next); } /** * Returns root of tree containing this node. */ final TreeNode<K,V> root() { for (TreeNode<K,V> r = this, p;;) { if ((p = r.parent) == null) return r; r = p; } }
上面两者的区别是,当一个链表小于64时,用链表,链表只能顺序写,当数据大于64时可以用二叉树存储,这样就方便快速查找。
3)定义Node 集合:
transient Node<K,V>[] table;
这里的table 是放的相同不同hash的head。
比如hash ==1 或者hash ==2 的分别放在Node【1】和node[2]
至于相同的hash的其他值,可以通过next 来组成一个链表。
总而言之就是构成多个链表,每个链表的hash相同的Node。也就是key的hash相同,
3)put 方法,主要是找到链表,然后找到链表的末尾,将末尾的next 指向 Node 就可以了。
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length; //初始化
if ((p = tab[i = (n - 1) & hash]) == null) //根据hash 找到链表,
//如果链表为空,表示没有数据,放在第一个位置
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
//按照树的结果来存储的。
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
//找到末尾位置:
if ((e = p.next) == null) {
//存放在末尾
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash); //当链表大于门槛时采用树存储。
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize(); //扩容链表
afterNodeInsertion(evict);
return null;
}
4)resize,这个主要是将链表的个数增加,增加个数之后,hash规则肯定变化,所以需要重新导入一份数据到新的的table。
final Node<K,V>[] resize() {
//前面一段主要是看增加到多少链表
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
//移动数据开始
table = newTab;
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
5)get:get是put的反过程,同样先根据hash找到分支,然后再找具体的数据。
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null) {
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
里面还有很多方法和内容就不全部看了,看得太多太累。懂结构就行
阅读全文
0 0
- jdk 源码分析(1)java hashmap的结构
- jdk 源码分析(2)java hashtable的结构及hashMap对比
- JDK源码学习之HashMap (一) : 底层存储结构分析
- JDK源码学习(1)-HashMap源码分析,HashMap与HashTable的差别
- 源码分析---HashMap的底层结构
- jdk 源码分析(4) java Set 结构
- jdk 源码分析(5)java ConcurrentSkipListMap结构
- jdk 源码分析(6)java BitSet结构
- jdk 源码分析(7)java ReentrantLock结构
- jdk源码分析之HashMap
- jdk源码分析之HashMap
- JDK源码-HashMap死锁分析
- [Java]JDK源码学习(3)HashMap
- java核心基础--jdk源码分析学习--HashMap
- JDK源码系列(2)----HashMap源码分析
- Java HashMap 源码分析
- java HashMap源码分析
- Java源码分析:HashMap
- 跨域请求 JSONP & CORS
- JStorm与Storm源码分析(七)--BasicBoltExecutor与装饰模式
- jquery autoComplete 自定义回写样式
- 动态代理
- 姿态解算(二),姿态更新
- jdk 源码分析(1)java hashmap的结构
- sbc(三)自定义Starter-SpringBoot重构去重插件
- 51nod-1299 监狱逃离(贪心)
- javascript高级程序设计--第五章基本包装类型开始
- Nginx教程-location配置
- Nginx 反向代理 Tomcat
- HDU
- HDU 2888 Check Corners(二维RMQ)
- 基于 CentOS Mysql 安装与主从同步配置详解