LinkedList源码解析
(注意:本博客与其他框架集合的文章解析的均使用 JDK1.8 的源码)
一、继承与实现
(1)继承了如下类:
AbstractSequentialList<E> 抽象类 此类提供的骨干实现List界面最小化以实现此接口
由“连续访问”数据存储备份所需的工作(如链接列表)。 对于随机访问数据(如数组), AbstractList应优先于此类。
(2)实现了如下接口:
List<E> 接口 该接口可以精确控制列表中每个元素的插入位置,
用户可以通过整数索引(列表中的位置)访问元素,并搜索列表中的元素。
Deque<E> 接口 支持两端元素插入和移除的线性集合。大多数Deque实现对它们可能包含的元素的数量没有固定的限制,
但是该接口支持容量限制的deques以及没有固定大小限制的deques。
Cloneable 克隆接口 不实现Cloneable接口的实例上调用对象的克隆方法导致抛出异常CloneNotSupportedException。
Serializable 序列化接口 不实现此接口的类将不会在任何状态序列化或反序列化。可序列化类的所有子类型都是可序列化的。
二、变量
以 transient 作为开头
transient 大家可以这样去理解,就是不会被序列化保存
int size = 0; 这个表示容量大小,在后面这个解释会越来越清晰
Node<E> first 表示第一个
Node<E> last 表示最后一个
此Node类是作为LinkedList的 静态泛型内部类 用来private修饰,表示不会被外部访问,供内部使用
E item
Node<E> next 表示 “下” 一个节点
Node<E> prev 表示 “上” 一个节点
三、构造函数
(1)无参构造
(2)有参构造 (将另一个集合的数据添加到当前集合里)
查看 addAll方法
( 这里的有参构造为什么会调用无参构造? 可能是为了方便以后再创建LinkedList需要做统一的初始化,在无参构造做了一些初始化后,其他构造函数只需要调用它即可完成初始化,当然,这只是博主的猜测而已)
四、方法
(分别讲解增删改查,以及常用的方法)
1、添加方法
(下图是JDK1.8 API帮助文档的集合(LinkedList)添加方法(add)图)
从上图可以看出除了 add 和 addAll 方法外,还增加了 addFirst 和 addLast方法 分别表示添加到头部,添加到尾部
(注意:变量 modCount 是来自 AbstractList 抽象类,表示 修改内部机构的次数)
(1) public boolean add(E e) { … }
方法作用: 将指定的元素追加到此列表的末尾。
方法过程:
1、调用 linkLast方法 执行存放元素数据
2、return 放回 true 表示添加成功
(2) public void add(int index, E element) { … }
方法作用: 将指定的元素插入到此列表中的指定位置。
方法过程:
1、调用 checkPositionIndex方法 检查索引是否在指定元素范围之内(就是存放数据的范围)
2、判断 指定的索引 是否等于 链表中的元素数量
(1)结果为 true:调用 linkLast方法,将元素插入到最后的位置
(2)结果为 false:调用 linkBefore方法,将元素插入到指定位置
(3)public boolean addAll(Collection<? extends E> c) { … }
方法作用:按照指定集合的迭代器返回的顺序将指定集合中的所有元素追加到此列表的末尾。
方法过程:
1、调用 addAll方法 将传入的集合插入到本链表中
2、return 返回 addAll方法的返回值,代表是否添加成功
(4)public boolean addAll(int index, Collection<? extends E> c) { … }
方法作用:将指定集合中的所有元素插入到此列表中,从指定的位置(index)开始。
方法过程:
1、调用 checkPositionIndex方法 检查index下标位置
2、将集合转换为数组
3、检查数组长度是否为零
4、判断 (插入指定集合元素)index 是否等于 size(实际元素的数量)
最后succ放入插入链表位置+1的节点、pred放入succ的上一个节点,如果插入下标不是最后位置的话
5、通过foreach循环将要插入的集合一个个转换成节点放入链表
6、将 插入的集合彻底 对接链表( 双方的下一个和上一个节点互相对接)
7、将原有的size元素数据 加上 新的数组元素
8、增加修改内部结构次数
(5)public void addFirst (E e) { … }
方法作用:在列表的开头插入指定的元素。
方法过程:
1、调用 linkFirst方法进行添加元素
(1)将原来的第一个节点存储起来
(2)构建node节点
(3)将添加到第一位的元素赋值给first
(4)如果原来的第一位元素是null,则代表链表本身就是null的,就可以直接进行赋值
(5)如果不是null,则将新值赋值给 原来的first节点的prev(上一位节点)
(6)增加实际数据数量
(7)增加修改内部结构数
(6)public void addLast (E e) { … }
方法作用:将指定的元素追加到此列表的末尾。
方法过程:
1、调用 linkLast方法进行添加元素
(1)将原来的最后一个节点存储起来
(2)构建node节点
(3)将添加到最后一位的元素赋值给first
(4)判断最后一位元素是否为null,如果是,则直接赋值
(5)如果不是null,则将新值赋值给 原来的last节点的next(下一位节点)
(6)增加实际数据数量
(7)增加修改内部结构数
2、删除方法
(下图是JDK1.8 API帮助文档的集合(LinkedList)删除方法(remove)图)
(1)public E remove () { … }
方法作用:检索并删除此列表的头部(第一个元素)。
方法过程:
1、调用删除头部的removeFirst方法
(2)public E removeFirst () { … }
方法作用:从列表中移除并返回第一个元素。
方法过程:
1、存储第一个节点元素
2、检查该节点是否为null(如果null,则抛出异常)
3、调用 unlinkFirst方法 后返回删除的值
(1)存储(删除的节点值、下一个节点值)
(2)将节点的属性赋值为null,好让GC更容易回收
(3)将下一任节点当做第一个节点
(4)检查下一个节点是否为null
----------如果null,则赋值nulll
----------如果不是,则将下个节点的“上一个节点”prev赋值null
(5)减少size的实际数据数量
(6)增加修改内部次数
(7)返回删除节点的值
(3)public E removeLast () { … }
方法作用:从列表中移除并返回最后一个元素。
方法过程:
1、存储最后一个节点元素
2、检查该节点是否为null(如果null,则抛出异常)
3、调用 unlinkLast方法 后返回删除的值
(1)存储(删除的节点值、上一个节点值)
(2)将该节点的属性赋值为null,好让GC更容易回收
(3)将上一任节点当做最后一个节点
(4)检查下一个节点是否为null
----------如果null,则赋值nulll
----------如果不是,则将上个节点的“下一个节点”next赋值null
(5)减少size的实际数据数量
(6)增加修改内部次数
(7)返回删除节点的值
(4)public E remove (int index) { … }
方法作用:移除列表中指定位置的元素。
方法过程:
1、检查下标是否越界
2、根据下标获取指定的元素传入 unlink方法
(1)存储 即将删除x节点的所有属性值
(2)当prev(上一个节点)为null
--------表示删除的是第一个元素,所以只需要覆盖first即可
--------如果不是,则将上一个节点的下一个节点赋值为当前元素的下一个节点
(3)当next(下一个节点)为null
--------表示删除的是最后一个元素,所以只需要覆盖last即可
--------如果不是则将下一个节点的上一个节点赋值为当前元素的上一个节点
(4)将删除节点的值赋值null
(5)减少size的实际数据数量
(6)增加修改内部次数
(7)返回删除节点的值
(5)public boolean remove (Object o) { … }
方法作用:从列表中删除指定元素的第一个出现项(如果存在)。
方法过程:
1、判断删除的元素值是否为null
-----如果null,则foreach循环所以node节点数据,只要出现一次node节点值是null的则进行删除,并且返回删除的值
-----如果不是null,则foreach循环所以node节点数据,只要出现一次node节点值是和传入的值相匹配,则进行删除,并且返回删除的值
(6)public boolean removeFirstOccurrence (Object o) { … }
方法作用:删除此列表中指定元素的第一个出现项(当从头到尾遍历列表时)。
方法过程:
1、调用 remove方法 进行删除第一次出现的值
(7)public boolean removeLastOccurrence(Object o) { … }
方法作用:删除此列表中指定元素的最后一次出现(当从头到尾遍历列表时)。
方法过程:
1、判断删除的元素值是否为null
-----如果null,则foreach循环所以node节点数据,只要出现一次node节点值是null的则进行删除,并且返回删除的值
-----如果不是null,则foreach循环所以node节点数据,只要出现一次node节点值是和传入的值相匹配,则进行删除,并且返回删除的值
3、修改方法
(下图是JDK1.8 API帮助文档的集合(LinkedList)修改方法(set)图)
方法作用:用指定的元素替换列表中指定位置的元素。
方法过程:
1、检查index下标是否越界
2、获取index下标对应的节点
3、修改节点值
4、返回修改节点之前的值
4、查询方法
(下图是JDK1.8 API帮助文档的集合(LinkedList)查询方法(get)图)
(1)public E get (int index) { … }
方法作用:返回列表中指定位置的元素。
方法过程:
1、检查下标是否越界
2、根据下标获取对应的节点,返回节点值
(2)public E getFirst () {… }
方法作用:返回列表中的第一个元素。
方法过程:
1、获取第一个元素节点并且判断是否为null,如果null,则抛出异常
2、否则就返回元素值
(3)public E getLast () { … }
方法作用:返回列表中的最后一个元素。
方法过程:
1、获取最后一个元素节点并且判断是否为null,如果null,则抛出异常
2、否则就返回元素值
5、常用方法
(1)public void clear () { … }
方法作用:返回列表中的最后一个元素。
方法过程:
1、清除节点之间的所有链接是“不必要的”,但是如果丢弃的节点驻留,则可以帮助分解GC
2、将第一个和最后一个节点赋值为null
3、将size实际数据数量赋值为0
4、增加修改内部结构次数
(2)public boolean contains(Object o) { … }
方法作用:如果此列表包含指定的元素,则返回true
方法过程:
1、根据indexOf获取指定元素的下标,如果该元素不存在,则返回-1
(3)public int size() { … }
方法作用:返回列表中元素的数量。
方法过程:
1、直接返回size元素实际数量。
6、结语
以上则是ArrayList必要重要的源码解析,本博客还有其他源码解析,如果还没更新,请静待更新
(如有看到有错误或者有疑问的地方,请在下方评论留言,谢谢)