当前位置:主页 > 查看内容

一文带你了解ArrayList底层机制

发布时间:2021-09-25 00:00| 位朋友查看

简介:java集合之ArrayList 相信大家在日常生活开发中接触的集合当中ArrayList是大家经常会用到的 但是想要在码界立足只是单纯的应用已经满足不了现在的需求了谁不怀念当初大学在校园操场上牵着好多小妹妹的手在遛弯好了不多说了泪水已经打湿了我的眼眶…(要说装逼……

java集合之ArrayList

相信大家在日常生活开发中,接触的集合当中,ArrayList是大家经常会用到的, 但是想要在码界立足,只是单纯的应用已经满足不了现在的需求了,谁不怀念当初大学在校园操场上牵着好多小妹妹的手在遛弯,好了,不多说了,泪水已经打湿了我的眼眶…(要说装逼这方面,我们谁不会,哈哈…,傻瓜,好好学技术),进入正题。

一:什么是ArrayList
大家应该都知道,他的底层其实就是一个数组结构的,当我们在使用list的时候相比大家都是直接类似new ArrayList<>(), 这种方式来进行创建,我们在new的时候会发生什么呢?(这里只针对JDK1.8来说)
大家都明白ArrayList的初始容量是10, 但是这个10是什么时候给的呢?我们明明new的空的list,咋会是10呢?难道new初始化的时候会直接给容量吗?我们继续看
在这里插入图片描述
显然不是new的时候给的,那么当我add的时候呢?这里我先直接给你上add时的源码图
在这里插入图片描述
这里面的size属性,是成员变量,大家都知道new对象的时候,有一步是对变量赋默认值,暖心的我在这里帮你们回忆一下,new对象的时候会发生什么。首先第一步会判断对应的类是否已经加载、连接、初始化(这里是当虚拟机遇到new指令的时候,他会去metaSpace找对应的类的符号引用,检查这个符号引用是否已经加载、连接、初始化);第二步会在堆中给他开辟一个内存空间(注意这里会处理并发安全问题,一般会采用cas失败重试机制,区域加锁保证更新原子性和每个线程预先分配一块TLAB,默认下TLAB一般占eden的1%;第三步会去默认初始化;第四步设置对象头;第五步执行init方法初始化。
好了我们继续,所以此时size=0。 然后我们进入ensureCapacityInternal方法,
在这里插入图片描述
这个方法里有calculateCapacity方法,我们再进去
在这里插入图片描述
到这里我们是不是很明白了,elementData是空,进入if,DEFAULT_CAPACITY是10,在类成员中定义的。接着我们看ensureExplicitCapacity方法,此时入参是10
在这里插入图片描述
modCount++,这个有得很关键,下面我回说到;if里会有一个grow操作,我们继续看一下这个方法如下:
在这里插入图片描述
相信大家看到这知道这个是在干嘛了吧,minCapacity是10,然后我们通过把oldCapacity数组扩大为原来的1.5倍,这里采用的右移位。如果newCapacity的容量此时与minCapacity做差,小于0就是赋值minCapacity给newCapacity。
Arrays.copyOf(elementData, newCapacity);这个操作是将elementData值放到一个新数组里,新数组的长度大小是newCapacity。然后我们再回到刚才的add方法,elementData[size++] = e;是将你此时add的元素放在数组中,此时add操作完成。这就是给集合添加元素的流程。

二:异常问题(ConcurrentModificationException)
由于公司代码保密原因,这里我只是用案例模拟下:
在这里插入图片描述
这是用iterator迭代器来遍历集合,如果发现值是2,就对list进行一个remove操作。这里大家感觉逻辑没问题,但是会报并发修改异常,我们来揭秘下。
首先list.iterator()方法里面就是直接 new Itr(),我们看下Itr()方法
在这里插入图片描述

里面有三个参数cursor(遍历当前元素的下标值),lastRet(遍历当前元素的下标的前一个值),expectedModCount = modCount(我前面说过modCount很重要…,在这里边表现,代表修改的次数,比如上面add就会对他+1,每对集合修改一次,就会+1,注意此时上面我们add了两次,所以modCount为2,expectedModCount为2),当我们调用hasNext方法时候,cursor != size()这个判断意思是,cursor集合当前下标如果不等于集合长度,证明集合中会有数据,我们就允许去获取值。然后就是 iterator.next()方法我们在来剖析
在这里插入图片描述
checkForComodification这个方法我们先不去看,看下面的,将cursor复制给i,然后获取数据返回,然后我们判断如果这个值是2,我们上面就会进行remove操作。我们看下这个操作源码
在这里插入图片描述
最终会起执行fastRemove方法,移除元素,可不是直接remove的值啊,最终还是根据值去寻找的下标然后我们去remove的下标。我们再进去fastRemove方法。
在这里插入图片描述
这里modCount又进行了加++操作,此时modCount为3,我们继续往下看
int numMoved = size - index - 1,这里其实就是寻找的你删除的元素的下标之后的元素个数,如果numMoved 是大于0的,我们会进行 System.arraycopy操作,这个操作不用我多解释吧?其实就是对数组进行了一个复制操作。这里最后
elementData[–size] = null这个操作也许你看到以后会说,这是什么傻逼操作,为啥还给弄成null(泪水再一次打湿我的眼眶…),是的,这里为null,是有意义的,是为了让他及时进行垃圾回收,释放空间。你想,删除操作其实就是对后面的元素进行复制到原数组,那我们数组里最后一个元素的空间是不是还会被一直占用啊?这就是典型的内存泄漏,举个例子就是占着茅坑不拉屎,你想憋死他们啊…。
好了,你吹了这么多牛逼,那这个异常发生的点在哪呢?不装逼了,往下看。大家还记得我说的checkForComodification方法吗?我们进去看一下
在这里插入图片描述
卧槽!!! 一目了然,这里循环的时候再进行下一次next的时候,会判断这两个数相不相等,不相等就会抛出异常。上面modCount是3,expectedModCount是2,显然不等,所以报错。
在这里插入图片描述
写到这里大家估计能了解到这个ArrayList东西了吧,哪里有不对的地方欢迎大家指导,虚心求学,我经常有一句话,不奋斗的人生是没有任何意义的,我们共同加油。

;原文链接:https://blog.csdn.net/SSDNSJT/article/details/116042693
本站部分内容转载于网络,版权归原作者所有,转载之目的在于传播更多优秀技术内容,如有侵权请联系QQ/微信:153890879删除,谢谢!
上一篇:ARP欺骗原理及实验 下一篇:没有了

推荐图文


随机推荐