年轻人不讲武德,一起聊聊List集合(三)
文章目录
- 前言
- 一、List类图
- 二、源码剖析
- 1. Vector(此篇详解)
- 2. ArrayList
- 3. LinkedList
- 4. CopyOnWriteArrayList
- ~~ 码上福利
前言
业精于勤荒于嬉,行成于思毁于随;
在码农的大道上,唯有自己强才是真正的强者,求人不如求己,静下心来,开始思考…
今天一起来聊一聊 List集合,看到这里,笔者懂,大家莫慌,先来宝图镇楼 ~
年轻人,不讲武德,敢偷袭我老同志,耗子尾汁…
咳咳… 相信大家满脑子的ArrayList已被保国爷爷经典的画面以及台词冲淡了,那么,目的已达到,那我们言归正传,对于屏幕前帅气的猿友们来说,ArrayList,LinkedList,Vector,CopyOnWriteArrayList… 张口就来,闭眼能写,但是呢,我相信大部分的猿友们并没有刨根问底真正去看过其源码,此时,笔者帅气的脸庞似有似无洋溢起一抹微笑,毕竟是查看过源码的猿,就是那么的不讲武德,吃我一记闪电五连鞭,话不多说,来吧,展示…
一、List类图
二、源码剖析
1. Vector(此篇详解)
在讲Vector集合之前呢,有必要嘱咐屏幕前的猿友一声,其实呢,Vector集合与ArrayList集合基本类似,但也存在差异,重点在于其对应构造以及新增、获取、删除方法,一定要认真仔细观阅,希望再文章末尾,猿友们已自行找出其两者异同点;
- 构造函数
// Vector底层为数组protected Object[] elementData;// 自定义扩容增量protected int capacityIncrement;/*** 无参构造*/public Vector() {this(10);}/*** 有参构造一* @param initialCapacity:指定数组初始容量*/public Vector(int initialCapacity) {this(initialCapacity, 0);}/*** 有参构造二* @param initialCapacity* @param capacityIncrement:指定自定义扩容增量,后续扩容中有具体体现*/public Vector(int initialCapacity, int capacityIncrement) {// 父类AbstractList无参构造 - 无具体实现super();// 数组初始容量校验if (initialCapacity < 0)throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);// 初始化数组 - length:10this.elementData = new Object[initialCapacity];// 设置自定义扩容增量this.capacityIncrement = capacityIncrement;}
从源码中可以看出,上述构造方法中,不论是无参构造方法,还是有参构造方法一最终都会调用有参构造方法二,其包含两个参数(initialCapacity:数组初始容量,capacityIncrement:自定义扩容增量);
结论:
构造初始化对象,初始化数组,默认length为10,自定义扩容增量默认为0;
- add() - 添加元素方法
// 记录对Vector操作次数protected transient int modCount = 0;// 记录数组元素个数protected int elementCount;// Vector最大元素个数private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;/*** 入口 - synchronized修饰:线程安全*/public synchronized boolean add(E e) {// 操作次数++modCount++;// 对数组进行扩容ensureCapacityHelper(elementCount + 1);// 数组添加元素elementData[elementCount++] = e;return true;}// 判断是否需要进行扩容 minCapacity:第一次add为(0+1)=1private void ensureCapacityHelper(int minCapacity) {// 最小容量-数组长度>0:需要进行扩容// 第1次add时:1-10 < 0,无需扩容// 第11次add时:11-10 > 0,需进行扩容if (minCapacity - elementData.length > 0) {// 具体扩容方法grow(minCapacity);}}// 具体扩容方法private void grow(int minCapacity) {// 获取数组长度int oldCapacity = elementData.length;// 计算新的数组容量;capacityIncrement:自定义扩容增量,用户可通过有参构造自定义其值,默认为0// 当用户自定义capacityIncrement且值大于0,扩容后数组容量为:数组长度+capacityIncrement// 反之(包含:用户自定义但值<=0 或 用户未定义),扩容后数组容量为:数组长度+数组长度,即扩容后为之前数组长度的2倍int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);// 判断如果扩容后长度-最小容量<0,扩容后的长度为最小容量,此判断作用于第一次添加元素时if (newCapacity - minCapacity < 0)newCapacity = minCapacity;// 计算扩容后长度最大值,最大值为Integer的最大值(2^31-1)if (newCapacity - MAX_ARRAY_SIZE > 0) {if (minCapacity < 0)throw new OutOfMemoryError();newCapacity = (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;}// 使用 Arrays.copyOf 对我们数组容量实现扩容elementData = Arrays.copyOf(elementData, newCapacity);}
从源码中可以看出,添加元素时且会对数组进行扩容;
知识点:
第一次扩容是在第11次add时,此时分为两种情况:
- 1.用户自定义capacityIncrement且值大于0时:数组长度扩容为(当前数组长度+capacityIncrement),之后每次扩容遵循此规则;
- 2.用户未定义capacityIncrement(代表默认情况下) 或 用户自定义capacityIncrement且值小于等于0时:数组长度扩容为(当前数组长度+当前数组长度),之后每次扩容遵循此规则;
结论:
默认情况下,每次扩容后为之前数组长度的2倍;
最大值:Integer最大值(2^31-1),最小值:10;
- get() - 获取元素方法
/*** 入口 - synchronized修饰:线程安全*/public synchronized E get(int index) {// 校验是否越界if (index >= elementCount)throw new ArrayIndexOutOfBoundsException(index);// so easy:通过下标获取元素return elementData(index);}// 通过下标获取元素E elementData(int index) {return (E) elementData[index];}
从源码中可以看出,获取元素时就是获取数组元素,通过下标直接获取即可;
- remove() - 删除元素方法
/*** 入口 - synchronized修饰:线程安全*/public synchronized E remove(int index) {// 操作次数++modCount++;// 校验下标是否越界if (index >= elementCount)throw new ArrayIndexOutOfBoundsException(index);// 获取要删除的元素E oldValue = elementData(index);/*** public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);* 说明此方法参数作用:* src:源数组* srcPos:源数组要复制的起始位置* dest:目的数组* destPos:目的数组放置的起始位置* length:复制的长度*/// 对应参数中lengthint numMoved = elementCount - index - 1;// 删除元素其实就是一个数组整体移动的过程,再将最后一个元素置空即可if (numMoved > 0) {// 此每个参数都需各位猿友细品下,慢慢来,只是一个过程... 如此如此,这般这般,暖男的我在下方提供图,便于猿友们理解System.arraycopy(elementData, index+1, elementData, index, numMoved);}// 将最后一个元素置空,如只有一个元素,置空即可,便于GC工作elementData[--elementCount] = null; // Let gc do its work// 返回删除的元素值return oldValue;}
相信猿友们已经看出来了,Vector的删除元素方法与ArrayList的删除元素方法是一样的,而且除了删除元素方法,其增加元素方法、获取元素方法也都是很相似的;
从源码中可以看出,删除元素实则为数组移动覆盖的过程,已下图为例,便于大家理解:
- 源数组:
- 目标数组(删除元素后的数组):
- 删除下标为0的元素(不)
结合 arraycopy(Object src, int srcPos, Object dest, int destPos, int length)来讲,可得知:
- src:为上述源数组;
- srcPos:源数组要复制的起始位置为(index+1 = 0+1 = 1)
- dest:为上述目标数组
- destPos:目标数组放置的起始位置为(index=0);
- length:复制的长度为(size-index-1 = 4-0-1 = 3)
- 过程演示:
- 划重点:
相信之前没仔细研究过的猿友们,对Vector删除元素大概过程已有一些了解;
但对于有经验的开发猿来说,笔者大概能猜到两种,一种是一心追随本心道心坚固的猿友,另一种呢就是追求大道审视局势的猿友;
前者:看到这里,不论是从笔者的描述还是图文结合的理解,貌似有一定的道理,但当时的我看并不是如此,既然是arraycopy,那就不应该是移动覆盖,而是重新复制一个新数组。
后者:我当时查阅源码好像觉得也并不是这样的,记得也是复制一个新数组,而不是移动覆盖。但笔者描述确又很在理,难道…遗漏了什么?
邪魅一笑,嘴角微起,来吧,展示…
其实嘛,大家说的都没错,实际上确实是复制新的数组,但Vector这里,源数组和目标数组是用一个呢.
哎…人生么,如此这般,细节决定成败。
- Vector总结:
- 底层为数组;
- 构造初始化,数组为空数组,集合size为0,数组length为0;
第一次扩容也就是第11次add时:默认情况下,数组长度length扩容为20,集合size为11;
默认情况下,之后每次扩容遵循此规则,oldCapacity + oldCapacity,故每次扩容为之前数组长度的2倍;
最大值:Integer最大值2147483647(2^31-1),最小值:10;- 通过下标去获取元素,故查询效率高,增删效率低;
- 线程安全;
- 有modCount;
2. ArrayList
不讲武德,一起聊聊List集合之ArrayList
3. LinkedList
不讲武德,一起聊聊List集合之LinkedList
4. CopyOnWriteArrayList
不讲武德,一起聊聊List集合之CopyOnWriteArrayList
~~ 码上福利
大家好,我是猿医生:
在码农的大道上,唯有自己强才是真正的强者,求人不如求己,静下心来,扫码一起学习吧…
年轻人不讲武德,一起聊聊List集合(三)相关推荐
- 年轻人不讲武德,TDengine边缘侧数据存储方案挑战SQLite
上周,涛思数据与EMQ在线上Meetup上联合发布了工业互联网一体化解决方案,基于TDengine.EMQ X搭建一个集工业数据采集.汇聚.清洗.存储分析以及可视化展示等能力于一体的轻量级边缘计算工业 ...
- 年轻人不讲武德有多可怕?
1 孩子,走你! ▼ 2 这都是什么阴间燃料 (素材来源网络,侵删) ▼ 3 年轻人不讲武德 (素材来源网络,侵删) ▼ 4 当你偷瞄喜欢的男生 ▼ 5 贝多芬:我入土这么都多年了 (素材来源网络,侵 ...
- cout不明确什么意思_年轻人不讲武德是什么梗和意思 年轻人不讲武德梗出处
太极大师马保国的一句"年轻人不讲武德,偷袭我这个69岁的老人家"最近火了,很多人都在模仿这个句式,用法还挺广泛.那么年轻人不讲武德是什么梗?年轻人不讲武德梗的出处是什么?下面小编带 ...
- 年轻人不讲武德,一起聊聊List集合(一)
文章目录 前言 一.List类图 二.源码剖析 1. ArrayList(此篇详解) 2. LinkedList 3. Vector 4. CopyOnWriteArrayList ~~ 码上福利 ...
- 年轻人不讲武德,一起聊聊List集合(五)
文章目录 前言 一.List类图 二.集合总结 1. ArrayList与Vector集合区别 2. ArrayList与CopyOnWriteArrayList集合区别 3. 时间复杂度 / 空间复 ...
- 再见,米哈游!原神社区防f12控制台调试代码全解(年轻人不讲武德)
前言 刚刚逛原神社区,不经意间按到了 f12 打开了控制台,突然屏幕暗了,发生甚么事了! 我一看,嗷,原来是进到无限 debbuger 调试了,传统审查讲究点到为止,用了 debbuger 这还了得, ...
- 年轻人不讲武德,竟用Python让马老师表演闪电五连鞭!
11月份的头条,是属于马保国的. 一位69岁的老同志,惨遭年轻人偷袭,不讲武德. 看看把老同志欺负的... 要不是马老师讲仁义讲道德,甩手就是一个五连鞭. 哈哈哈,所以本期我们就用Python给马保国 ...
- 年轻人不讲武德!卢伟冰脱口秀:小米高端之路好自为之
11月26日,Redmi正式发布千元精品Note系列新机,三剑齐发:Note 9 Pro.Note 9和Note 9 4G三款产品. Redmi Note 9 Pro国内首发旗舰规格的一亿像素相机HM ...
- 年轻人不讲武德不仅白piao接口测试知识还白piao接口测试工具会员
朋友们好啊!我是骑着白马的少年!!! 今天有个同学问我,马哥,马哥在吗?我说什么事儿. 啪啪给我打了一堆字,附加给我一张截图.我一看!噢,原来是接口测试,还要引用变量参数. 他说两种方法,一种是叫开发 ...
最新文章
- Linux网站架构系列之Apache----进阶篇
- 使用Python命令创建jenkins的job
- MFC对话框中处理Enter或Esc按键事件方法
- Vuejs——组件——slot内容分发
- Unity3D面试ABC
- Notepad++的json 格式化
- Qt学习笔记-基于QGraphicsScene的打地鼠游戏
- 微信扫码登录注册实现
- shape中的ring标签的使用方法
- 查看微信小程序网页源代码
- CodeCanyon上的20种最佳WordPress登录表单
- 屏幕录制软件哪个好?
- 报错:array_column函数未定义(原因:php版本问题)
- 360“隐私保护器”真相
- 无锡地铁三号线列车时刻表(2020-10-22)
- 《赋得古原草送别》诗改文
- c#语言编写汉诺塔游戏,c#语言编写汉诺塔游戏
- [actions] -- actions详细使用
- 开发人员的涅槃重生路——出差
- OmniPlan 3破解码