EFAULT_LOAD_FACTOR是默认的负载因子=0.75;
MAXIMUM_CAPACITY是最大HashMap容量;
threshold为一个阈值大小是(capacity * load factor)即容量*负载因子;
程序员
1)方法一: public HashMap()
无参构造函数会默认构造出一个初始容量为16,负载因子为0.75的空HashMap;
数组
2) 方法二:public HashMap(int initialCapacity, float loadFactor)
第二种构造方法会构造出一个初始容量大于或者等于initialCapacity的2的幂次方,负载因子为loadFactor空HashMap;
bash
3)方法三:public HashMap(int initialCapacity)
方法三调用方法二,默认负载因子为0.75;
微信
4)方法四:public HashMap(Map<? extends K, ? extends V> m)
数据结构
方法四构造一个和指定的Map有相同的Mappings的HashMap,默认的负载因子为0.75;
app
源码:
函数
tableSizeFor方法返回大于initialCapacity的最近的2的幂次方值。 从源码中咱们看到n获得的是cap-1,这步操做主要是为了防止cap已是2的幂次方的状况, 后面一系列n |= n>>> 一、二、四、五、16;无符号右移操做,使得n的二进制低位全为1, 最后判断n是否大于MAXIMUM_CAPACITY,大于则返回MAXIMUM_CAPACITY,不然返回n+1即为2的幂次方。
源码分析
源码:
ui
putMapEntries()的功能为将传入的Map初始化为一个HashMap结构, 转化前先判断m的大小,若是为0则不必初始化,不然将Map中的元素一次将其放进HashMap, 此时分为两种状况: <1>table没有初始化 在这种状况下,咱们须要检查map的大小是否超过MAXIMUM_CAPACITY,以后初始化threshold为相应的大小。 <2>table已经初始化 这种状况下须要检查s是否大于当前的threshold阈值,若是大于则resize(),不然不需扩容,只需将m中每一个元素放进HashMap便可。
this
putMapEntries中的resize()
resize就是从新将原来HashMap内的数据放进一个新扩容的HashMap中,
而旧容器中的数据存放位置有两种状况,
<1>在原来的位置上
<2>在原来的位置+oldCap的位置上
当咱们HashMap中的数据量达到threshold时,便可进行resize()扩容;
源码:
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;
} 复制代码
哈希冲突简单来说就是两次数据通过一系列计算得到的哈希值相同,
哈希冲突有三种状况:
<1>当两个节点的key值相同时,此时必定发生哈希冲突,
此时将key相应的value值换成新值
<2>当两个节点key值不一样,但由于哈希函数的运算致使最后的哈希值相同致使的冲突,
此时将value值放进此哈希值内单链表或者红黑树中
<3>两节点key值不一样,哈希值不一样,可是对数组长度取模后获得的值相同
遵循状况2的处理方式
首先从实现的接口来看HashMap和Hashtable都实现了Map、Cloneable(可复制)、Serializable(可序列化)这三个接口,
而HashMap是继承自AbstractMap类,HashTable是继承自Dictionary类。
补充:
1.sychronized意味着在一次仅有一个线程可以更改Hashtable。就是说任何线程要更新Hashtable时要首先得到同步锁,其它线程要等到同步锁被释放以后才能再次得到同步锁更新Hashtable。
2.HashMap中,null能够做为键,这样的键只有一个;能够有一个或多个键所对应Value值为null。
3.由于HashTable自己有synchronized因此意味着一次只能有一个线程改变HashTable 4.HashMap首先须要根据元素的 KEY计算出一个hash值,而后再用这个hash值来计算获得最终的位置。 Hashtable直接使用对象的hashCode。hashCode是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值。而后再使用除留余数发来得到最终的位置。
更多技术文章请关注微信公众号:Java程序员汇集地