List集合
List接口

ArrayList
结论
底层原理
执行的代码
1.无参构造
elementData
就是ArrayList
底层元素存放的数组
2.有参构造
两个只是构造方法不一样,但是后面的
add
和grow
是一样的只不过是直接将
elementData初始化大小为8的数组
![]()
3.集合
add
方法![]()
判断当前的
capacity
容量是否充足,size
当前集合的元素的数量(每一次进入 add 方法的时候,size的位置就是存放add的元素的下标,存放完之后 size++),第一次 add ,size 为 0
进入
ensureCapacityInternal
确认容量内部 的方法,modCount
记录当前的集合被修改次数,防止多个线程修改,抛出异常,fail-fast如果 if 成立,就表示 需要的最小容量已经大于当前容量了,所以当前容量需要扩大,进入
grow
扩容
minCapacity
指的是源数组中存储的元素个数加上将要存储的元素个数,这里第一次add()时,minCapacity为1,数组的长度为0,数组内存不够,故进行扩充
elementData.length
是数组(集合)容量的大小![]()
扩容,进入
grow
方法,最后通过Array.copyOf
方法复制旧数组并且扩容返回新的数组
右移,相当于是除以2,左移,相当于是乘以2
所以
newCapacity = oldCapacity + (oldCapacity >> 1)
就是相当于 扩容了
oldCapacity
的1.5倍下面代码的第一个
if
判断,是因为第一次扩容为 10 的时候,minCapacity=10,newCapacity、oldCapacity 都为0
,这是对一次进行扩容的弥补措施![]()
ArrayList和Vector区别

Vector
线程安全的,synchronized
加锁
底层原理
执行代码
1.无参构造
this
就是调用本类的 构造器
所以,我们可以指定 有参构造,或者两个参数的构造方法
capacityIncrement
是第一次进行扩容的大小2.进入
add
方法跟
ArrayList
大体一样
3.满足条件。进入
grow
扩容方法
capacityIncrement
扩容的大小,创建vector
没有指定的话,默认是 0那么
capacityIncrement
一辈子就都是 0 了,所以每一次扩容都是oldCapacity + oldCapacity
,2倍如果创建
vector
的时候指定了capacityIncrement
,那么之后的每一次扩容都是按照capacityIncrement
大小进行扩容,就不是 2 倍了
LinkedList
双向链表
底层原理
执行代码
1.无参构造
就是简单的初始化 属性
有参数的构造
E 是创建 linkList 对象指定的泛型
2.
add
方法![]()
linkLast
方法就是,将元素 element 添加到 linkList 链表的 last 位置就是一个简单的算法逻辑,就是 l 是复制的 last,然后创建一个新的节点,存储 add 元素,新节点的 prev 指向 l(也就是上次的last),然而 last 被赋值为新节点 newNode
这就完成了 last 还是 last,并且 newNode 的 prev 指向的是上一次的 last
![]()
3.
remove
方法默认的是 删除第一个节点
进入
unlinkFirst
删除节点的方法,返回值是 删除的元素 element如果不加 final 的话,同样会被 gc 干掉
![]()
ArrayList和LinkedList区别
两者都是线程不安全的
链表因为只能从头节点开始遍历,只能通过顺次指针访问,查询效率低,所以 查询和修改某一个节点 性能较差
因为 链表 不同node节点对象是通过 指针 连接起来的,是不连续的内存地址
链表则必须是顺序访问,不能随机访问(必须从头节点开始进行遍历,因为不知道内存地址)
底层维护的是 Node 对象
链表可以随意扩大容量
ArrayList
底层是维护的一个数组,数组是连续的内存地址,所以使用方便 ,查询效率 比链表 高数组可以随机访问其中的元素 (因为内存地址是连续的,可以通过计算得出访问的元素的地址)
数组则不能随便扩容,只能通过 copy 重新创建一个数组进行转移扩容
本文来自博客园,作者:小小俊少,转载请注明原文链接:https://www.cnblogs.com/xxjs168/articles/17481743.html