一些理解

Java中,一个int型变量,普通的Integer,在内存里是以32位的bit存在的,第一个bit是符号位,0表示正数,1表示负数。

所以最大的int数,加1后变成了最小的数???本来是正的最多,现在变成了负的最多,是为最小的数。

最小的int数是32个位都是1,第一位是1表示是负数(负数当然比正数小啦!!),后面31个1表示负的最多,所以最小。然额,据说在计算机里,负数是是补码形式存在,补码=(第一位符号位除外的)反码+1。

so, 这一串32个1是为最小的int数,虽然壮观,想要是计算机里表示,请出示补码!!好,

HashMap

containsValue方法:
用两个for循环遍历整个map,一个一个对比找,可想而知性能一般。
containsKey方法:
使用key进行hash操作(用key的hashCode()方法获取hashCode,以二进制形式,把原hashCode与右移16位后的hashCode进行^操作,也就是异或,相同取0,不同取1),得到hash,这个hash的二进制形式再和内部数组(很多人称为桶)的容量-1(桶初始容量为16)的二进制形式(16-1=15,15的二进制是1111)做与操作,相当于取hash的最后4位,然后变成2进制后,就是桶的索引(就是数组下标),这个位置上是一个链表或者红黑树结构,依次取出来,看该元素的key是不是和传入的key equals,是的话表示通过这个key找到了对应的数据,表示当前HashMap里是有这个key的,否则表示没有这个key。get和put两个方法也是用同一方式找到要操作的键值对的。
resize方法:
内部会判断是不是还有扩容的空间(如果容量已经大于HashMap允许的最大容量了(MAXIMUM_CAPACITY = 1 << 30),就没的扩了,一边玩去吧,容量就这样了)。第一次容量16,当键值对个数(也就是HashMap里总共有多少对数据)超过12时(也就是75%,后续的扩容时机也一样),会扩容,扩容成原容量的2位,因为代码里是直接把容量进行左移1位运算。
比较有趣的是扩容后的复制机制。
首先,遍历整个桶,桶里的每个元素是个链表(或红黑树),也遍历,这时候有4个指针,分别是 索引不变头(loHead), 索引不变尾(loTail), 索引变动头(hiHead), 索引变动尾(hiTail)。
如何判断这个链表里的元素索引要不要变呢?前面提到过,桶容量-1的二进制形式和key的hash做与操作后可得桶索引,那现在要知道桶索引有没有改变,新的桶容量新增的那一位上对应的hash是0还是1,0则索引还是不会改变,如果是1则索引改变。那如何知道新增的那一位是啥呢?这个时候,老的那个容量就派上用场了,把hash和老容量(而不是老容量-1)做与操作(老容量是16的话,那不就是第5位是1,其它都是0嘛,所以就看hash的第5位咯,为什么这样呢?因为新的容量不是32嘛,到时候真要存数据的话,对key做hash后做与操作取索引是用32-1=31的二进制取索引的啊,31不就是5个1嘛,hash和5个1取与操作,和hash和4个1取与操作,有没有差异就取决于多出来那一位),如果得到0,就表示那一位是0,否则就是1嘛,是1就会改变桶索引嘛。设计这个进制的人可能真的有高深的数学头脑,膜拜。
接下来就比较好理解了,链表里的元素一个个拿出来,判断下,不会改变桶索引,则用不变头(loHead)和不变尾(loTail)操作它,要不然就用变动头(hiHead)和变动尾(hiTail)操作它,依次看下一个有没有有没有,会不会改桶索引,好好的挂起来,挂在链表上。
不改桶索引的,最后就还放这个位置,要改桶索引的,它的索引地址现在来看也是确定了的,就是原来的索引加上原来的容量,因为容量是左移了一位嘛,减1后的二进制来取索引就会从hash上多取一个1,多取的那个1表示的就是原桶的容量了。
红黑树的这里不讲因为我也目前有点忘记了。
 
 
原文地址:https://www.cnblogs.com/lihan829/p/9535854.html