HashMap大致数据结构
public class test {
public static void main(String[] args) {
//创建一个HashSet对象
HashSet set = new HashSet();
for (int i = 1 ;i <= 20; i++){
//添加元素
set.add(i);
}
System.out.println(set);
}
}
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
public HashSet() {
//创建一个HashMap对象
map = new HashMap<>();
}
}
看到没有!HashMap,接着往下看
2. map = new HashMap<>();
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
{
//指定数组的初始大小为16. 1<<4=1*2*2*2*2
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
//构造函数
public HashMap() {
/*给loadFactor赋值 0.75
用来计算数组的临界点下标
*/
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
}
3. set.add(i);
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
//添加一个元素 返回boolean类型表示成功或失败
public boolean add(E e) {
//PRESENT——表示map<K,V>中的value,这里为一个Object对象,K=e——在键中存储要添加的元素
return map.put(e, PRESENT)==null;
}
}
4. map.put(e, PRESENT)
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
{
public V put(K key, V value) {
//hash(key)用来计算hash值,最终得到将要存储元素的下标
//key,value即Map的键值对
return putVal(hash(key), key, value, false, true);
}
}
5. putVal(hash(key), key, value,false,true);
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
{
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
//这些为工具变量,起到逻辑控制作用。没有太过特殊的意义
Node<K,V>[] tab; Node<K,V> p; int n, i;
//table = Map结构中的数组初始为空
if ((tab = table) == null || (n = tab.length) == 0)
//一开始初始化准备添加第一个元素,上面条件成立,先看下resize代码 -> 6.1
n = (tab = resize()).length;
//如果通过hash算法得到的下标没有存储任何元素,则将元素通过Node存放在当前下标
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
/*判断如果当前要存储hash值与计算出的hash值相等并且将要存储的元素的地址或内容与
原下标的地址或内容相等则执行
*/
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 {
//hash值相同,其他上述条件不成立时.循环数组当前下标的链表中的元素
for (int binCount = 0; ; ++binCount) {
//之所以要判断链表下一节点是否指向空,是因为要将新的节点添加到链表尾部
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
/*判断如果当前下标的链表中节点的数量>=7(从0开始数),则考虑将链表
转为红黑树
*/
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;
}
}
//如果e不是null,返回的也不是null 表示添加失败
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);
//返回null 表示添加成功
return null;
}
}
6.1. resize()
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
{
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
//oldCap=0,因为数组现在为null
int oldCap = (oldTab == null) ? 0 : oldTab.length;
//oldThr=0,默认为0 —数组长度临界值old
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 {
//程序来到这
//得到默认为数组指定的大小 16
newCap = DEFAULT_INITIAL_CAPACITY;
//得到默认为数组指定的临界点下标 16 * 0.75 = 12
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"})
//创建一个长度为16的Node类型数组——Node对象可形成链表
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
//将创建的数组赋予Map中的table数组
table = newTab;
/*...省略代码段...*/
return newTab;
}
}
补充:
当链表的节点到8个并且数组的长度>=64,链表会转为红黑树;
如果节点>8,但是数组的长度<64,则先对数组进行扩容,达到64时,会重新计算hash值并将数据结构转为红黑树;
Map中存储的元素在达到临界值的时候,会自动*2扩容;这里的元素指Map中元素的总个数,不单单是数组中的元素,也包括链表中的元素。
END
原文:https://www.cnblogs.com/isclay/p/15311887.html