LinkedList源码阅读笔记

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!

// 这样index在后半段的时候可以少遍历一半的元素

if (index < (size >> 1)) {

// 如果是在前半段

// 就从后往前遍历

Node x = first;

for (int i = 0; i < index; i++)

x = x.next;

return x;

} else {

//如果是在前半段

//就从前往后遍历

Node x = last;

for (int i = size - 1; i > index; i–)

x = x.prev;

return x;

}

}

添加元素


  • 头插法

private void linkFirst(E e) {

// 首节点

final Node f = first;

// 创建新节点,新节点的next是首节点

final Node newNode = new Node<>(null, e, f);

first = newNode;

// 判断链表是不是为空

// 如果是就把last也置为新节点

// 否则把原首节点的prev指针置为新节点

if (f == null)

last = newNode;

else

f.prev = newNode;

//元素个数加1

size++;

// 修改次数 +1,用于 fail-fast 处理

modCount++;

}

public void addFirst(E e) {

linkFirst(e);

}

  • 尾插法

void linkLast(E e) {

//尾结点

final Node l = last;

//新节点

final Node newNode = new Node<>(l, e, null);

//尾结点置为新节点

last = newNode;

//如果链表为空,头结点指向尾结点

if (l == null)

first = newNode;

else

l.next = newNode;

//元素个数加1

size++;

// 修改次数 +1,用于 fail-fast 处理

modCount++;

}

public void addLast(E e) {

linkLast(e);

}

public boolean add(E e) {

linkLast(e);

return true;

}

在链表头部和尾部插入时间复杂度都是O(1),头插法和尾插法的示意图如下:

在这里插入图片描述

  • 中间插入法:中间插入需要找到插入位置节点,改变该节点的前趋和该节点前趋节点的后继

在这里插入图片描述

//根据索引插入节点

public void add(int index, E element) {

//判断是否越界

checkPositionIndex(index);

//未插入

if (index == size)

linkLast(element);

else

//找到索引位置节点,在该节点前插入新节点

linkBefore(element, node(index));

}

// 在节点succ之前添加元素

void linkBefore(E e, Node succ) {

//节点succ的前趋节点

final Node pred = succ.prev;

//新节点

final Node newNode = new Node<>(pred, e, succ);

//改变节点succ的前趋指向

succ.prev = newNode;

// 判断前置节点是否为空

// 如果为空,说明是第一个添加的元素,头结点重新赋值

// 否则修改前置节点的next为新节点

if (pred == null)

first = newNode;

else

pred.next = newNode;

//元素个数加1

size++;

// 修改次数 +1,用于 fail-fast 处理

modCount++;

}

在中间添加元素效率低一些,首先要先找到插入位置的节点,再修改前后节点的指针,时间复杂度为O(n)。

删除元素


双链表中删除元素只需要改变前趋和后继的指向。

  • 删除头节点

//删除头节点

public E removeFirst() {

final Node f = first;

//如果链表为空,抛出异常

if (f == null)

throw new NoSuchElementException();

// 删除首节点

return unlinkFirst(f);

}

// 删除头节点

private E unlinkFirst(Node f) {

// 头结点

final E element = f.item;

//头结点后继节点

final Node next = f.next;

//头结点数据域后继置空,帮助GC

f.item = null;

f.next = null;

//头结点置为后继节点

first = next;

// 如果只有一个元素,删除了,把last也置为空

// 否则把next的前趋置为空

if (next == null)

last = null;

else

next.prev = null;

size–;

modCount++;

//返回删除的节点

return element;

}

  • 删除尾结点

//删除尾结点

public E removeLast() {

//尾结点

final Node l = last;

//链表为空,抛出异常

if (l == null)

throw new NoSuchElementException();

return unlinkLast(l);

}

//删除尾结点

private E unlinkLast(Node l) {

// 尾结点元素

final E element = l.item;

//尾结点前趋节点

final Node prev = l.prev;

//尾结点数据、前趋置为null,帮助GC

l.item = null;

l.prev = null;

//尾结点置为前趋节点

last = prev;

// 如果只有一个元素,删除了把first置为空

// 否则把前置节点的next置为空

if (prev == null)

first = null;

else

prev.next = null;

size–;

modCount++;

//返回删除的节点

return element;

}

注意:

不管是上一节的头插入和未插入,还是这一节的删除头节点和删除尾结点,都没有在List中定义。前面提到,LinkedList实现了Deque接口,所以这是作为双向队列的LinkedList插入和删除元素的方式。还有获取头结点和尾结点的方法getFirst()和getLast(),同样都是双向队列的实现。

  • 删除指定位置的节点

在这里插入图片描述

//删除指定位置的节点

public E remove(int index) {

//检查越界情况

checkElementIndex(index);

//根据索引找到节点,删除

return unlink(node(index));

}

//删除指定节点

E unlink(Node x) {

// 删除节点的值

final E element = x.item;

//被删除节点的后继节点

final Node next = x.next;

//被删除节点的前趋节点

final Node prev = x.prev;

// 如果前趋节点为空

// 说明是首节点,让first指向x的后继节点

// 否则修改前置节点的next为x的后继节点

if (prev == null) {

first = next;

} else {

prev.next = next;

x.prev = null;

}

// 如果后继节点为空

// 说明是尾节点,让last指向x的前趋节点

// 否则修改后置节点的prev为x的前趋节点

if (next == null) {

last = prev;

} else {

next.prev = prev;

x.next = null;

}

// 清空x的元素值,协助GC

x.item = null;

// 元素个数减1

size–;

// 修改次数加1,fail-fast

modCount++;

//返回删除的元素

return element;

}

删除头尾节点,时间复杂度为O(1)。

在中间删除元素,首先要找到删除位置的节点,再修改前后指针,时间复杂度为O(n)。

前面还提到,LinkedList可以作为栈使用,栈的特点是先进后出,LinkedList同样有作为栈的方法实现。

push


入栈:插入头节点

public void push(E e) {

addFirst(e);

}

pop


出栈:删除头结点

public E pop() {

return removeFirst();

}

与ArrayList

============================================================================

LinkedList作为Java中链表的实现,ArrayList作为顺序表的实现(ArrayList源码阅读笔记),LinkedList常常被拿来和ArrayList来进行比较。

LinkedList、ArrayList基本操作时间效率对比如下(粗略对比):

| 操作 | ArrayList | LinkedList |

| — | — | — |

| get(int index) | O(1) | O(n),平均 n / 4步 |

| add(E element) | 最坏情况(扩容)O(n) ,平均O(1) | O(1) |

| add(int index, E element) | O(n) ,平均n / 2步 | O(n),平均 n / 4步 |

最后

针对最近很多人都在面试,我这边也整理了相当多的面试专题资料,也有其他大厂的面经。希望可以帮助到大家。

image

上述的面试题答案都整理成文档笔记。 也还整理了一些面试资料&最新2021收集的一些大厂的面试真题(都整理成文档,小部分截图)

image

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!
常常被拿来和ArrayList来进行比较。

LinkedList、ArrayList基本操作时间效率对比如下(粗略对比):

| 操作 | ArrayList | LinkedList |

| — | — | — |

| get(int index) | O(1) | O(n),平均 n / 4步 |

| add(E element) | 最坏情况(扩容)O(n) ,平均O(1) | O(1) |

| add(int index, E element) | O(n) ,平均n / 2步 | O(n),平均 n / 4步 |

最后

针对最近很多人都在面试,我这边也整理了相当多的面试专题资料,也有其他大厂的面经。希望可以帮助到大家。

[外链图片转存中…(img-SVpD6j75-1714702707791)]

上述的面试题答案都整理成文档笔记。 也还整理了一些面试资料&最新2021收集的一些大厂的面试真题(都整理成文档,小部分截图)

[外链图片转存中…(img-MrYnT0ke-1714702707792)]

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!

  • 30
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值