LinkedList的基本结构是双向链接的直线结构。

链表的构造函数有两个,其中空构造函数什么都没做,就是一个空实现。

    /**
* Constructs an empty list.
*/
public LinkedList() {
} /**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}

另外就是Node的组成

  private static class Node<E> {
E item;
Node<E> next;
Node<E> prev; Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}

可以发现顺序是前驱,值,后继。

继续使用断点调试的方法阅读源码

 LinkedList linkedList = new LinkedList();
linkedList.add(1);

目前还是空链表,我们准备加1.

public boolean add(E e) {
linkLast(e);
return true;
}

继续跳转到linkLast(e)中

    /**
* Links e as last element.
*/
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;//last指向newNode
if (l == null)//因为刚开始的last就是空,也就是一开始就是空链表
first = newNode;那么first也指向newNode
else
l.next = newNode;
size++;
modCount++;
}

首先last指向l,l此时为null,而l也指向了这个null。newNode为前驱和后继均为null,值为1的节点,。那么此时头节点就是当前节点就是尾节点。要不然的话,我们在执行

  linkedList.add(2);

此时再到

void linkLast(E e) {
final Node<E> l = last;//此时last指向的是值为1的节点,也是链表的尾节点
final Node<E> newNode = new Node<>(l, e, null);//创造新的节点,新节点的前驱是原先的尾戒点,
last = newNode;//然后将last指向新的节点,也就是后移last。
if (l == null)
first = newNode;
else//此时l不为空
l.next = newNode;//所以l的下一个连上新的节点,这样就加入成功了
size++;
modCount++;
}

LinkedList的get()方法,下标是从0开始算的

public E get(int index) {
checkElementIndex(index);
return node(index).item;
}

首先越界检查,然后跳到node()方法。

    /**
* Returns the (non-null) Node at the specified element index.
*/
Node<E> node(int index) {
// assert isElementIndex(index); if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}

这段代码就很有意思,我们都知道链表增删都很快,但是查询较慢。此处的查询做了优化。也就是说如果索引小于链表长度的一半,那么就从头开始找。反之从后找。这样就提高了效率。

接下来就是add(int index, E element)方法。进断点调试

LinkedList linkedList = new LinkedList();
linkedList.add(1);
linkedList.add(2);
linkedList.add(3);
linkedList.add(4);
linkedList.add(5);
linkedList.add(3,9);
 public void add(int index, E element) {
checkPositionIndex(index); if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}

可以看到首先是越界检查。然后是判断是否是直接插入队尾。如果是插入队尾。如果不是,则先node(idnex)查询出索引处的节点。然后再进入到linkBefore

    void linkBefore(E e, Node<E> succ) {此时succ为插入处的节点,也就是值为4的节点。e值为9
// assert succ != null;
final Node<E> pred = succ.prev;//保存4的前驱,
final Node<E> newNode = new Node<>(pred, e, succ);//此时新的节点的前驱要为4的前驱,后继为4.
succ.prev = newNode;//此时再连上4之后的节点,也就是4的前驱是新节点。
if (pred == null)//这里是链接3的后继。如果4的前驱是空,那么就是在对头插入数据,那么first就是新的节点
first = newNode;
else//否则,3的next就是新的节点,此时就全部连上了
pred.next = newNode;
size++;
modCount++;
}

接下来就是remove(),默认删除第一个节点。

public E remove() {
return removeFirst();
}

很明显就看出来是删除第一个节点了。

   public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
 private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}

首先是保存first。存储头节点的后续节点next。然后令头节点值为空,头节点断开。再移动first到next。如果只有头节点,那么last也为空,此时链表为空。如果不是,则next的前驱为null,那么就删除了头节点了。

接下来是remove(int index)方法。

  public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}

一样,先检查越界,然后查找索引处的节点,再到unlink函数中

E unlink(Node<E> x) {//此时x为值为3的节点
// assert x != null;
final E element = x.item;//保存删除的节点的值用于返回
final Node<E> next = x.next;//保存当前节点的后续节点
final Node<E> prev = x.prev;//保存当前节点的前驱节点 if (prev == null) {//如果前驱节点为空
first = next;//那么就代表删除的是头节点,所以first指向next
} else {//否则
prev.next = next;前驱的节点指向后续的节点。
x.prev = null;再断开当前节点,此时前驱连上了后续,但是后续还没连前驱
} if (next == null) {这里就是后续连前驱。
last = prev;
} else {
next.prev = prev;
x.next = null;
} x.item = null;
size--;
modCount++;
return element;
}

由于调试是

 LinkedList linkedList = new LinkedList();
linkedList.add(1);
linkedList.add(2);
linkedList.add(3);
linkedList.add(4);
linkedList.add(5);
linkedList.add(3,9);
linkedList.get(3);
// linkedList.remove();//默认删除第一个节点
linkedList.remove(2);//删除第二个节点

所以上述unlink中的注释就可以看懂了

接下来将indexof(Object o)

public int indexOf(Object o) {
int index = 0;
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}

这就是查找节点对象的索引,如果没找到就返回-1.

最后将clear()函数。

 public void clear() {
// Clearing all of the links between nodes is "unnecessary", but:
// - helps a generational GC if the discarded nodes inhabit
// more than one generation
// - is sure to free memory even if there is a reachable Iterator
for (Node<E> x = first; x != null; ) {
Node<E> next = x.next;
x.item = null;
x.next = null;
x.prev = null;
x = next;
}
first = last = null;
size = 0;
modCount++;
}

循环依次断开每个头节点。然后fist和last都指向null,大小为0.

总结:1.LinkedList是非线程安全的

2.他的删除增加效率很高,但是查询较慢。

最新文章

  1. 在linux平台实现atosl
  2. hibernate映射文件
  3. 登陆中session的处理
  4. selenium+python自动化之登录案例
  5. Android Drawable体系
  6. ACdream OJ 1153 (k-GCD)
  7. 清华集训2014 day2 task1 简单回路
  8. gbs build使用说明
  9. unity3d打开对话框
  10. cookie机制和session机制的区别(面试题)
  11. Codeforces 242E:XOR on Segment(位上的线段树)
  12. k8s经典实战—搭建WordPress
  13. 2018-2019-2-20175332-实验二《Java面向对象程序设计》实验报告
  14. ELK的安装
  15. python全栈开发笔记---------函数
  16. ES6语法知识
  17. iOS 检测网络状态 自动判断 认为提示网络改变
  18. CTF-练习平台-Misc之 Convert
  19. hashmap实现及哈希冲突
  20. R语言提取字符串的一部分substring函数

热门文章

  1. npm clear folder
  2. GitHub Actions in Action
  3. leetcode solution cracked tutorial
  4. modal 遮罩层,滚动穿透 bug
  5. WebAssembly JavaScript APIs
  6. 智能合约稳定币USDN的价值在哪里?
  7. JULLIAN MURPHY:拥有良好的心态,运气福气便会自来
  8. MySQL学习04(DQL查询)
  9. Redis 日志篇:系统高可用的杀手锏
  10. 若依管理系统RuoYi-Vue(三):代码生成器原理和实战