3.3> nextIndex和prevIndex
- 我们先来看第一个红框中的方法nextIndex(i, len),其实通过该方法,我们还可以引出prevIndex(i, len)方法。源码和注释如下所示:
【解释】
通过源码,我们可以得出以下结论:
nextIndex就是从指定的下标i开始,向后获取下一个位置的下标值。
preIndex就是从指定的下标i开始,前向获取上一个位置的下标值。
那么,如果越界了怎么办呢?它们会采用循环查找法。即:获取队尾的下一个下标就会返回队首的下标;获取队首的上一个下标就会返回队尾的下标。如下所示:
3.4> 开放地址法
- 当我们看到这里,就发现这个ThreadLocalMap有些“奇怪”,它并没有按照我们之前在学习HashMap的方式去解决哈希冲突,即:数组 链表。而它其实使用的是一种叫做“开放地址法”作为解决哈希冲突的一种方式。
- 什么是开放地址法呢?
开放地址法的基本思想就是:一旦发生了冲突,那么就去寻找下一个空的地址;那么只要表足够大,空的地址总能找到,并将记录插入进去。
- ThreadLocalMap和HashMap的区别是什么呢?
- HashMap
- 数据结构是数组 链表
- 通过链地址法解决hash冲突的问题
- 里面的Entry内部类的引用都是强引用
- ThreadLocalMap
- 数据结构仅仅是数组
- 通过开放地址法来解决hash冲突的问题
- Entry内部类中的key是弱引用,value是强引用
- HashMap
- 链地址法和开放地址法的优缺点是什么呢?
- 开放地址法
- 容易产生堆积问题,不适于大规模的数据存储。
- 散列函数的设计对冲突会有很大的影响,插入时可能会出现多次冲突的现象。
- 删除的元素是多个冲突元素中的一个,需要对后面的元素作处理,实现较复杂。
- 链地址法:
- 处理冲突简单,且无堆积现象,平均查找长度短。
- 链表中的结点是动态申请的,适合构造表不能确定长度的情况。
- 删除结点的操作易于实现。只要简单地删去链表上相应的结点即可。
- 指针需要额外的空间,故当结点规模较小时,开放定址法较为节省空间。
- 开放地址法
- ThreadLocalMap采用开放地址法原因是什么?
- ThreadLocal往往存放的数据量不会特别大(而且key 是弱引用又会被垃圾回收,及时让数据量更小)。
- 采用开放地址法简单的结构会更节省空间,同时数组的查询效率也是非常高,加上第一点的保障,冲突概率也比较低。
- 那么,当我们了解了开发地址法的原理之后,我们就有了看懂下面代码的重要武器了。好,那我们继续看下面的源码
后面的内容,参见:ThreadLocal源码精讲(3)