前言
业精于勤荒于嬉,行成于思毁于随;
在码农的小道上,唯有本人强才是真正的强人,求人不如求己,静下心来,开始思考…
明天一起来聊一聊 List汇合,看到这里,笔者懂,大家莫慌,先来宝图镇楼 ~
我要打十个年轻人,敢偷袭我老同志,耗子尾汁..
咳咳.. 置信大家满脑子的ArrayList已被保国爷爷经典的画面以及台词冲淡了,那么,目标已达到,那咱们言归正传,对于屏幕前帅气的猿友们来说,ArrayList,LinkedList,Vector,CopyOnWriteArrayList... 张口就来,闭眼能写,然而呢,我置信大部分的猿友们并没有刨根问底真正去看过其源码,此时,笔者帅气的脸庞似有似无弥漫起一抹微笑,毕竟是查看过源码的猿,就是那么的不讲武德,吃我一记闪电五连鞭,话不多说,来吧,展现..
一、List类图
二、源码分析
1. ArrayList(此篇详解)
- 构造函数
// 默认值-空数组 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // ArrayList底层为数组 transient关键字:以后数组不能被序列化 transient Object[] elementData; /** * 构造函数 */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
从源码中能够看出,结构只为底层数组进行初始化,默认值为空数组;
- add() - 增加元素办法
// ArrayList元素个数 private int size; // 默认初始容量 private static final int DEFAULT_CAPACITY = 10; // 记录对ArrayList操作次数 protected transient int modCount = 0; // ArrayList最大元素个数 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; /** * 增加入口 */ public boolean add(E e) { // 对数组进行初始化以及扩容 ensureCapacityInternal(size + 1); // 数组中增加元素 elementData[size++] = e; return true; } // 1.对数组进行初始化以及扩容 private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } // 2.计算最小容量 private static int calculateCapacity(Object[] elementData, int minCapacity) { // 判断数组是否为空,即为第一次增加元素 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // max办法:a >= b ? a : b // eg: 10 >= 1 -> 10 return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } // 3.判断是否须要扩容 private void ensureExplicitCapacity(int minCapacity) { // modCount(记录批改次数) -> 在以后线程应用迭代器的过程中,如有其余线程批改了modCount(会判断是否批改操作有误(线程平安问题)) -> 抛出ConcurrentModificationException异样 // Fail-Fast 机制,疾速失败机制,modCount申明为volatile,保障线程之间批改的可见性 modCount++; // 判断是否进行扩容 if (minCapacity - elementData.length > 0) // 具体扩容办法 grow(minCapacity); } // 4.具体扩容 private void grow(int minCapacity) { // 获取数据长度 int oldCapacity = elementData.length; // 计算扩容后长度,第一次为例:0 + (0 >> 1) = 0 + 0 = 0 int newCapacity = oldCapacity + (oldCapacity >> 1); // 判断如果扩容后长度-最小容量<0,扩容后的长度为最小容量,此判断作用于第一次增加元素时 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; // 计算扩容后长度最大值,最大值为Integer的最大值(2^31-1) if (newCapacity - MAX_ARRAY_SIZE > 0) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); newCapacity = (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; } // 应用 Arrays.copyOf 对咱们数组容量实现扩容 elementData = Arrays.copyOf(elementData, newCapacity); }
从源码中能够看出,增加元素时且会对数组进行初始化或者扩容;
知识点:
第一次扩容也就是第一次add时:数组长度length扩容为10,汇合size为1;
第二次扩容也就是第十一次add时:数组长度length扩容为15,汇合size为11;
之后每次扩容遵循以下规定,oldCapacity + (oldCapacity >> 1);
论断:
每扩容后为之前数组长度的1.5倍,最大值:Integer最大值(2^31-1),最小值:10;
- get() - 获取元素办法
/** * 入口 */ public E get(int index) { // 校验是否越界 rangeCheck(index); // so easy:通过下标获取元素 return elementData(index); } // 校验下标是否越界 private void rangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size); }
从源码中能够看出,获取元素时就是获取数组元素,通过下标间接获取即可;
- remove() - 删除元素办法
/** * 入口 */ public E remove(int index) { // 校验下标是否越界 rangeCheck(index); // add()办法中已详情形容 modCount++; // 获取要删除的元素 E oldValue = elementData(index); /** * public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length); * 阐明此办法参数作用: * src:源数组 * srcPos:源数组复制的起始地位 * dest:目标数组 * destPos:指标数组搁置的起始地位 * length:数组元素被复制的数量 */ // 对应参数中length int numMoved = size - index - 1; // 删除元素其实就是一个数组整体挪动的过程,再将最初一个元素置空即可 if (numMoved > 0) // 此每个参数都需各位猿友细品下,慢慢来,只是一个过程... 如此如此,这般这般,暖男的我在下方提供图,便于猿友们了解 System.arraycopy(elementData, index+1, elementData, index, numMoved); // 将最初一个元素置空,如只有一个元素,置空即可,便于GC工作 elementData[--size] = null; // clear to let GC do its work // 返回删除的元素值 return oldValue; }
从源码中能够看出,删除元素实则为数组挪动笼罩的过程,已下图为例,便于大家了解:
- 源数组:
- 指标数组(删除元素后的数组):
- 删除下标为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)
- 过程演示:
- ==划重点:==
置信之前没认真钻研过的猿友们,对ArrayList删除元素大略过程已有一些理解;
但对于有教训的开发猿来说,笔者大略能猜到两种,一种是二心追寻本心道心坚硬的猿友,另一种呢就是谋求小道扫视局势的猿友;
前者:看到这里,不论是从笔者的形容还是图文联合的了解,貌似有肯定的情理,但过后的我看并不是如此,既然是arraycopy,那就不应该是挪动笼罩,而是从新复制一个新数组。
后者:我过后查阅源码如同感觉也并不是这样的,记得也是复制一个新数组,而不是挪动笼罩。但笔者形容确又很在理,难道...脱漏了什么?
邪魅一笑,嘴角微起,来吧,展现...
其实嘛,大家说的都没错,实际上的确是复制新的数组,但ArrayList这里,源数组和指标数组是用一个呢.
哎...人生么,如此这般,细节决定成败。
- ArrayList总结:
- 底层为数组;
- 结构初始化,数组为空数组,汇合size为0,数组length为0;
第一次扩容也就是第一次add时:数组长度length扩容为10,汇合size为1;第二次扩容也就是第11次add时:数组长度length扩容为15,汇合size为11;
之后每次扩容遵循以下规定,oldCapacity + (oldCapacity >> 1),故每次扩容为之前数组长度的1.5倍;
最大值:Integer最大值2147483647(2^31-1),最小值:10;
- 通过下标去获取元素,故查问效率高,增删效率低;
- 线程不平安;
- 有modCount;
2. LinkedList
不讲武德,一起聊聊List汇合之LinkedList
3. Vector
不讲武德,一起聊聊List汇合之Vector
4. CopyOnWriteArrayList
不讲武德,一起聊聊List汇合之CopyOnWriteArrayList
5. List汇合比照剖析-总结篇
不讲武德,一起聊聊List汇合之总结篇
~~ 码上福利
大家好,我是猿医生:
在码农的小道上,唯有本人强才是真正的强人,求人不如求己,静下心来,开始学习吧...