文章目录

  • 前言
  • 一、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)来讲,可得知:

  1. src:为上述源数组;
  2. srcPos:源数组要复制的起始位置为(index+1 = 0+1 = 1)
  3. dest:为上述目标数组
  4. destPos:目标数组放置的起始位置为(index=0);
  5. length:复制的长度为(size-index-1 = 4-0-1 = 3)
  • 过程演示:
  • 划重点:

相信之前没仔细研究过的猿友们,对Vector删除元素大概过程已有一些了解;

但对于有经验的开发猿来说,笔者大概能猜到两种,一种是一心追随本心道心坚固的猿友,另一种呢就是追求大道审视局势的猿友;

前者:看到这里,不论是从笔者的描述还是图文结合的理解,貌似有一定的道理,但当时的我看并不是如此,既然是arraycopy,那就不应该是移动覆盖,而是重新复制一个新数组。

后者:我当时查阅源码好像觉得也并不是这样的,记得也是复制一个新数组,而不是移动覆盖。但笔者描述确又很在理,难道…遗漏了什么?

邪魅一笑,嘴角微起,来吧,展示…

其实嘛,大家说的都没错,实际上确实是复制新的数组,但Vector这里,源数组和目标数组是用一个呢.

哎…人生么,如此这般,细节决定成败。

  • Vector总结:
  1. 底层为数组;
  2. 构造初始化,数组为空数组,集合size为0,数组length为0;
    第一次扩容也就是第11次add时:默认情况下,数组长度length扩容为20,集合size为11;
    默认情况下,之后每次扩容遵循此规则,oldCapacity + oldCapacity,故每次扩容为之前数组长度的2倍;
    最大值:Integer最大值2147483647(2^31-1),最小值:10;
  3. 通过下标去获取元素,故查询效率高,增删效率低;
  4. 线程安全;
  5. 有modCount;

2. ArrayList

不讲武德,一起聊聊List集合之ArrayList


3. LinkedList

不讲武德,一起聊聊List集合之LinkedList


4. CopyOnWriteArrayList

不讲武德,一起聊聊List集合之CopyOnWriteArrayList


~~   码上福利

大家好,我是猿医生:

在码农的大道上,唯有自己强才是真正的强者,求人不如求己,静下心来,扫码一起学习吧…

年轻人不讲武德,一起聊聊List集合(三)相关推荐

  1. 年轻人不讲武德,TDengine边缘侧数据存储方案挑战SQLite

    上周,涛思数据与EMQ在线上Meetup上联合发布了工业互联网一体化解决方案,基于TDengine.EMQ X搭建一个集工业数据采集.汇聚.清洗.存储分析以及可视化展示等能力于一体的轻量级边缘计算工业 ...

  2. 年轻人不讲武德有多可怕?

    1 孩子,走你! ▼ 2 这都是什么阴间燃料 (素材来源网络,侵删) ▼ 3 年轻人不讲武德 (素材来源网络,侵删) ▼ 4 当你偷瞄喜欢的男生 ▼ 5 贝多芬:我入土这么都多年了 (素材来源网络,侵 ...

  3. cout不明确什么意思_年轻人不讲武德是什么梗和意思 年轻人不讲武德梗出处

    太极大师马保国的一句"年轻人不讲武德,偷袭我这个69岁的老人家"最近火了,很多人都在模仿这个句式,用法还挺广泛.那么年轻人不讲武德是什么梗?年轻人不讲武德梗的出处是什么?下面小编带 ...

  4. 年轻人不讲武德,一起聊聊List集合(一)

    文章目录 前言 一.List类图 二.源码剖析 1. ArrayList(此篇详解) 2. LinkedList 3. Vector 4. CopyOnWriteArrayList ~~   码上福利 ...

  5. 年轻人不讲武德,一起聊聊List集合(五)

    文章目录 前言 一.List类图 二.集合总结 1. ArrayList与Vector集合区别 2. ArrayList与CopyOnWriteArrayList集合区别 3. 时间复杂度 / 空间复 ...

  6. 再见,米哈游!原神社区防f12控制台调试代码全解(年轻人不讲武德)

    前言 刚刚逛原神社区,不经意间按到了 f12 打开了控制台,突然屏幕暗了,发生甚么事了! 我一看,嗷,原来是进到无限 debbuger 调试了,传统审查讲究点到为止,用了 debbuger 这还了得, ...

  7. 年轻人不讲武德,竟用Python让马老师表演闪电五连鞭!

    11月份的头条,是属于马保国的. 一位69岁的老同志,惨遭年轻人偷袭,不讲武德. 看看把老同志欺负的... 要不是马老师讲仁义讲道德,甩手就是一个五连鞭. 哈哈哈,所以本期我们就用Python给马保国 ...

  8. 年轻人不讲武德!卢伟冰脱口秀:小米高端之路好自为之

    11月26日,Redmi正式发布千元精品Note系列新机,三剑齐发:Note 9 Pro.Note 9和Note 9 4G三款产品. Redmi Note 9 Pro国内首发旗舰规格的一亿像素相机HM ...

  9. 年轻人不讲武德不仅白piao接口测试知识还白piao接口测试工具会员

    朋友们好啊!我是骑着白马的少年!!! 今天有个同学问我,马哥,马哥在吗?我说什么事儿. 啪啪给我打了一堆字,附加给我一张截图.我一看!噢,原来是接口测试,还要引用变量参数. 他说两种方法,一种是叫开发 ...

最新文章

  1. Linux网站架构系列之Apache----进阶篇
  2. 使用Python命令创建jenkins的job
  3. MFC对话框中处理Enter或Esc按键事件方法
  4. Vuejs——组件——slot内容分发
  5. Unity3D面试ABC
  6. Notepad++的json 格式化
  7. Qt学习笔记-基于QGraphicsScene的打地鼠游戏
  8. 微信扫码登录注册实现
  9. shape中的ring标签的使用方法
  10. 查看微信小程序网页源代码
  11. CodeCanyon上的20种最佳WordPress登录表单
  12. 屏幕录制软件哪个好?
  13. 报错:array_column函数未定义(原因:php版本问题)
  14. 360“隐私保护器”真相
  15. 无锡地铁三号线列车时刻表(2020-10-22)
  16. 《赋得古原草送别》诗改文
  17. c#语言编写汉诺塔游戏,c#语言编写汉诺塔游戏
  18. [actions] -- actions详细使用
  19. 开发人员的涅槃重生路——出差
  20. OmniPlan 3破解码

热门文章

  1. novnc的安装和设置开机自启
  2. RestTemplate配置调用接口的请求头
  3. 连接mysql数据库的三个接口_数据库的三种接口
  4. 魔兽世界怀旧服服务器维护9-5,怀旧服维护完毕 时光徽章将暂停使用
  5. 搬砖的开始 HFSS入门 T型波导的内场分析
  6. 【Netty 从成神到升仙系列 大结局】全网一图流死磕解析 Netty 源码
  7. 关于使用二维数组实现输出三角形规律排列数字的Java代码
  8. 转 树莓派使用标准PC104键盘 设置
  9. python中的pylab_python-什么是%pylab?
  10. 在rawimage里展示出所选取的图片