前言
传统map中要求key的判断是equals
,当我们想用对象作为key,并且需要地址相同时才能认为是相等的,这样传统map做不了,我们就要打破这一规定,比较key值用==
- 为一组易变的对象维护代理对象
- 基于一个对象的引用建立一个快速缓存
- 保持一个有引用的对象的内存图
属性
/*
*
* 负载因子2/3,超过21的时候就要扩容,这个21也许是性能最佳点,所以指定21,
* 推算出32
*/
private static final int DEFAULT_CAPACITY = 32;
/*
* 最小扩容4,负载因子是2/3,最小4的时候,超过2就会扩容
*/
private static final int MINIMUM_CAPACITY = 4;
/*
*最大容量,2^31-1,要2次幂,2^30,但是key,value都在table中,所以它
* 只能装下hashMap一半的元素2^29
*/
private static final int MAXIMUM_CAPACITY = 1 << 29;
hash
确保hash是偶数,这样与运算长度,就能确保key落在偶数索引
。这样在table中,偶数是key,vlue在奇数上,避免出现下面的情况:
private static int hash(Object x, int length) {
int h = System.identityHashCode(x);
// Multiply by -254 to use the hash LSB and to ensure index is even
//相当于乘以-254,确保hash值是偶数,左移1位就可以保证
return ((h << 1) - (h << 8)) & (length - 1);
}
(h << 1) - (h << 8)
确保了最后一位是0,决定了hash值是偶数,这样key只会在偶数索引上(最后一位是0,和任何数&运算都是偶数),然后低8位除去最后一位前7位都得以保留,其他位混合,这样更有利于Hash均匀。
构造函数
public IdentityHashMap() {
//无参构造,默认32
init(DEFAULT_CAPACITY);
}
public IdentityHashMap(int expectedMaxSize) {
//指定大小,如果小于0,抛出非法参数错误
if (expectedMaxSize < 0)
throw new IllegalArgumentException("expectedMaxSize is negative: "
+ expectedMaxSize);
init(capacity(expectedMaxSize));
}
//key和value都存储在table,所以指定容量后,实际给table大小是2倍
private void init(int initCapacity) {
table = new Object[2 * initCapacity];
}
//按给定扩容阈值,超过它就会引发扩容
private static int capacity(int expectedMaxSize) {
// assert expectedMaxSize >= 0;
return
/**扩容阈值大于最大容量的1/3,直接给最大容量2^29创建,否则如果指定容量小于最小容量的
* 2/3,直接最小容量4。其他则table长度的值就应该是3/2倍的扩容阈值,那应该是1.5倍啊,
* 怎么是三倍?Integer.highestOneBit返回的是左侧最大2的次幂,已经减半了i - (i >>>
* 1),不就是0.5了吗,所以这里直接3倍了
*/
(expectedMaxSize > MAXIMUM_CAPACITY / 3) ? MAXIMUM_CAPACITY :
(expectedMaxSize <= 2 * MINIMUM_CAPACITY / 3) ? MINIMUM_CAPACITY :
Integer.highestOneBit(expectedMaxSize + (expectedMaxSize << 1));
}
返回给定值左侧
最大的2的次幂
//获取i最高位1代表的2次幂,最高位1代表的权值
public static int highestOneBit(int i) {
// HD, Figure 3-1
i |= (i >> 1);//将第二高位置1
i |= (i >> 2);//将第二高位之后的两位置1
i |= (i >> 4);
i |= (i >> 8);
i |= (i >> 16);//所有位都置1了
//此时的如果i+1就是i右侧最小的2次幂(HashMap)
return i - (i >>> 1);//i左侧最大的2次幂
}