来,一起阅读源码,通过LinkedList回顾基础( 二 )


还是最基础的知识:数组是一块连续的内存 , 每个元素分配固定大小 , 很容易定位到指定索引 。 而链表每个节点除了数据还有指向下一个节点的指针 , 内存分配不一定连续 , 要想知道某一个索引上的值 , 只能从头开始(或者从尾)一个一个遍历 。
RandomAccess接口是一个标记接口 , 没有任何方法 。 唯一的作用就是使用instanceof判断一个实现集合是否具有随机访问的能力 。
List list1 = new LinkedList();if (list1 instanceof RandomAccess) {//...}可能看到这里还是不大明白 , 不要紧 , 这个问题的关键其实就是ArrayList和LinkedList对List接口中get方法实现的区别 , 后文会专门讲到 。
变量//实际存储元素数量transient int size = 0;/** * 指向头节点 , 头结点不存在指向上一节点的引用 ** Invariant: (first == null /** * 指向尾节点 , 尾节点不存在指向下一节点的引用 * Invariant: (first == null //节点类型 , 包含存储的元素和分别指向下一个和上一个节点的指针private static class Node {E item;Node next;Node prev;Node(Node prev, E element, Node next) {this.item = element;this.next = next;this.prev = prev;}}注意这里的节点类型 , 可以看出 , LinkedList实现基于双向链表 。 为啥用不直接用单向链表一链到底呢?最主要的原因是为了查找效率 , 前面说到链表的查找效率比较低 , 如果是单向链表 , 按索引查找时 , 不管索引处于哪个位置 , 只能从头开始 , 平均需要N次;若是双向链表 , 先判断索引处于前半部分还是后半部分 , 再决定从头开始还是从尾开始 , 平均需要N/2次 。 当然 , 双向链表的缺点就是需要的存储空间变大 , 这从另一个方面体现了空间换时间的思想 。
上述两个变量 , first和last , 其本质是指向对象的引用 , 和Student s=new Student()中的s没有区别 , 只不过first一定指向链表头节点 , last一定指向链表尾节点 , 起到标记作用 , 让我们能够随时从头或者从尾开始遍历 。
构造函数//空参构造public LinkedList() {}//通过指定集合构造LinkedList ,调用addAll方法public LinkedList(Collection c) {this();addAll(c);}常用方法常用方法比较多(多到一张图截不下) , 这里主要分两类 , 一类是List体系 , 一类是Deque体系.
List体系下的方法:
来,一起阅读源码,通过LinkedList回顾基础文章插图
这里主要看两个 , add,get

  • add(E e)
添加元素到链表末尾 , 成功则返回true
//添加元素到链表末尾 , 成功则返回truepublic boolean add(E e) {linkLast(e);return true;}void linkLast(E e) {//1.复制一个指向尾节点的引用lfinal Node l = last;//2.将待添加元素构造为一个节点 , prev指向尾节点final Node newNode = new Node<>(l, e, null);//3.last指向新构造的节点last = newNode//4.如果最初链表为空 , 则将first指向新节点if (l == null)first = newNode;//5.最初链表不为空 , 则将添加前最后元素的next指向新节点elsel.next = newNode;//存储的元素数量+1size++;//修改次数+1modCount++;}关键在于linkLast(E e)方法 , 分两种情况 , 最初是空链表添加元素和最初为非空链表添加 。
这里涉及到的知识很基础 , 还是链表的基本操作 , 但是单纯用语言很难描述清楚 , 所以画个简图表示一下(第一次画图 , 没法尽善尽美 , 将就一下吧)
linkLast(E e)方法
双向链表的基本形式
来,一起阅读源码,通过LinkedList回顾基础文章插图
对于空链表的添加
对应linkLast(E e)方法注释1、2、3、4
空链表 , 没有节点 , 意味着first和last都指向null
来,一起阅读源码,通过LinkedList回顾基础文章插图
1.复制一个指向尾节点的引用l(蓝色部分)
来,一起阅读源码,通过LinkedList回顾基础文章插图
此时复制的引用l本质也指向了null
2.将待添加元素构造为一个节点newNode , prev指向l , 也就是null
来,一起阅读源码,通过LinkedList回顾基础文章插图
3.last指向新构造的节点(红色部分)
来,一起阅读源码,通过LinkedList回顾基础文章插图
4.最初链表为空 , 将first指向新节点
来,一起阅读源码,通过LinkedList回顾基础文章插图
此时 , first和last均指向唯一非空节点 , 当然引用newNode依然存在 , 但是已经没有意义 。
对于非空链表的添加
对应linkLast(E e)方法注释1、2、3、5