深入剖析LinkedList的底层源码,再也不怕面试官问了!,2024年最新java 面试题库

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

  •        (last.next == null && last.item != null)
    

*/

// 双向链表指向尾节点的指针

transient Node last;

}

从以上源码可以看出,LinkedList 的属性比较少。分别是:

  • size : 双向链表的节点个数

  • first: 双向链表指向头节点的指针

  • last: 双向链表指向尾节点的指针

注意:first 和 last 是由引用类型Node连接的,这是它的一个内部类。

Node源码剖析

private static class Node {

// item表示当前存储元素

E item;

// next表示当前节点的后置节点

Node next;

// prev表示当前节点的前置节点

Node prev;

Node(Node prev, E element, Node next) {

this.item = element;

this.next = next;

this.prev = prev;

}

}

LinkedList 是通过双向链表实现的,而双向链表就是通过Node类来实现的,Node类中通过item变量存储当前元素,通过next变量指向当前节点的下一个节点,通过prev变量指向当前节点的上一个节点。

二、构造方法及其源码剖析

1. 无参构造方法

源码剖析

/**

  • Constructs an empty list.

*/

public LinkedList() {

}

LinkedList 的无参构造就是构造一个空的list集合

2. Collection<? extends E>型构造方法

源码剖析

/**

  • 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©;

}

从源码中分析可得:

构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序。

三、常用方法及其源码剖析

1. add() 方法

add(E e) 方法,将指定的元素追加到此列表的末尾

源码剖析

/**

  • Appends the specified element to the end of this list.

  • This method is equivalent to {@link #addLast}.

  • @param e element to be appended to this list

  • @return {@code true} (as specified by {@link Collection#add})

*/

public boolean add(E e) {

linkLast(e);

return true;

}

其中,调用了linkLast()方法,设置元素e为最后一个元素

/**

  • Links e as last element.

*/

void linkLast(E e) {

// 获取链表的最后一个节点

final Node l = last;

// 创建一个新节点

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

// 使新的一个节点为最后一个节点

last = newNode;

// 如果最后一个节点为null,则表示链表为空,则将newNode赋值给first节点

if (l == null)

first = newNode;

else

// 否则尾节点的last指向 newNode

l.next = newNode;

// 元素的个数加1

size++;

// 修改次数自增

modCount++;

}

总结

  • 第一步,获取链表的最后一个节点
  • 第二步,创建一个新节点
  • 第三步,使新的一个节点为最后一个节点
  • 第四步,如果最后一个节点为null,则表示链表为空,则将newNode赋值给first节点;否则尾节点的last指向 newNode

add(int index, E element) 方法,在指定位置插入元素

源码剖析

/**

  • Inserts the specified element at the specified position in this list.

  • Shifts the element currently at that position (if any) and any

  • subsequent elements to the right (adds one to their indices).

  • @param index index at which the specified element is to be inserted

  • @param element element to be inserted

  • @throws IndexOutOfBoundsException {@inheritDoc}

*/

public void add(int index, E element) {

// 检查索引index的位置

checkPositionIndex(index);

// 如果index==size,直接在链表的最后插入元素,相当于add(E e)方法

if (index == size)

linkLast(element);

else

// 否则调用node方法将index位置的节点找出,接着调用linkBefore 方法

linkBefore(element, node(index));

}

总结:

  • 首先检查索引index的位置,看下标是否越界
  • 如果index==size,直接在链表的最后插入元素,相当于add(E e)方法
  • 否则调用node方法将index位置的节点找出,接着调用linkBefore 方法

其中,调用checkPositionIndex()方法,检查索引index的位置

源码剖析

private void checkPositionIndex(int index) {

if (!isPositionIndex(index))

throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

}

在增加元素的时候,调用了linkBefore()方法,在非null节点succ之前插入元素e

源码剖析

/**

  • Inserts element e before non-null Node succ.

*/

void linkBefore(E e, Node succ) {

// assert succ != null;

// 指定节点的前驱

final Node pred = succ.prev;

// 创建新的节点,前驱节点为succ的前驱节点,后续节点为succ,则e元素就是插入在succ之前的

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

// 构建双向链表,succ的前驱节点为新的节点

succ.prev = newNode;

// 如果前驱节点为null,则把newNode赋值给first

if (pred == null)

first = newNode;

else

// 构建双向列表

pred.next = newNode;

// 元素的个数加

size++;

// 修改次数自增

modCount++;

}

总结

  • 指定节点的前驱
  • 创建新的节点,前驱节点为succ的前驱节点,后续节点为succ,则e元素就是插入在succ之前的
  • 构建双向链表,succ的前驱节点为新的节点
  • 如果前驱节点为null,则把newNode赋值给first;否则构建双向列表
2. remove() 方法

remove() 方法 删除这个列表的头(第一个元素)

源码剖析

/**

  • Retrieves and removes the head (first element) of this list.

  • @return the head of this list

  • @throws NoSuchElementException if this list is empty

  • @since 1.5

*/

public E remove() {

return removeFirst();

}

其中,调用了removeFirst()方法,删除并返回第一个元素

源码剖析

/**

  • Removes and returns the first element from this list.

  • @return the first element from this list

  • @throws NoSuchElementException if this list is empty

*/

public E removeFirst() {

final Node f = first;

if (f == null)

throw new NoSuchElementException();

return unlinkFirst(f);

}

remove(int index) 方法,删除指定位置的元素

源码剖析

/**

  • Removes the element at the specified position in this list. Shifts any

  • subsequent elements to the left (subtracts one from their indices).

  • Returns the element that was removed from the list.

  • @param index the index of the element to be removed

  • @return the element previously at the specified position

  • @throws IndexOutOfBoundsException {@inheritDoc}

*/

public E remove(int index) {

// 检查索引index的位置

checkElementIndex(index);

// 调用node方法获取节点,接着调用unlink(E e)方法

return unlink(node(index));

}

总结

  • 检查索引index的位置
  • 调用node方法获取节点,接着调用unlink(E e)方法

其中,调用了unlink()方法

源码剖析

/**

  • Unlinks non-null node x.

*/

E unlink(Node x) {

// assert x != null;

// 获得节点的三个属性

final E element = x.item;

final Node next = x.next;

final Node prev = x.prev;

// 进行移除该元素之后的操作

if (prev == null) {

// 删除的是第一个元素

first = next;

} else {

prev.next = next;

x.prev = null;

}

if (next == null) {

// 删除的是最后一个元素

last = prev;

} else {

next.prev = prev;

x.next = null;

}

// 把item置为null,让垃圾回收器回收

x.item = null;

// 移除一个节点,size自减

size–;

modCount++;

return element;

}

3. set() 方法

set(int index, E element)方法,将指定下标处的元素修改成指定值

源码剖析

/**

  • Replaces the element at the specified position in this list with the

  • specified element.

  • @param index index of the element to replace

  • @param element element to be stored at the specified position

  • @return the element previously at the specified position

  • @throws IndexOutOfBoundsException {@inheritDoc}

*/

public E set(int index, E element) {

checkElementIndex(index);

// 通过node(int index)找到对应下标的元素

Node x = node(index);

// 取出该节点的元素,供返回使用

E oldVal = x.item;

// 用新元素替换旧元素

x.item = element;

// 返回旧元素

return oldVal;

}

总结:

先通过node(int index)找到对应下标的元素,然后修改Node中item的值。

4. get() 方法

get(int index) 返回此列表中指定位置的元素

源码剖析

public E get(int index) {

// 检查索引index的位置

checkElementIndex(index);

// 调用node()方法

return node(index).item;

}

在此调用了node()方法

源码剖析

/**

  • Returns the (non-null) Node at the specified element index.

*/

// 这里查询使用的是先从中间分一半查找

Node node(int index) {

// assert isElementIndex(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;

}

}

总结

这里查询使用的是先从中间分一半查找,根据下标是否超过链表长度的一半,来选择从前半部分开始遍历查找,还是从后半部分开始遍历查找。

  • 如果index小于size的一半,就从首节点开始遍历,一直获取x的下一个节点
  • 如果index大于或等于size的一半,就从尾节点开始遍历,一直获取x的上一个节点

四、双端队列操作方法的源码剖析

offerFirst(E e) 方法,将指定的元素插入到此集合列表的前面,也就是将将元素添加到首部

源码剖析

/**

  • Inserts the specified element at the front of this list.

  • @param e the element to insert

  • @return {@code true} (as specified by {@link Deque#offerFirst})

  • @since 1.6

*/

分享

这次面试我也做了一些总结,确实还有很多要学的东西。相关面试题也做了整理,可以分享给大家,了解一下面试真题,想进大厂的或者想跳槽的小伙伴不妨好好利用时间来学习。学习的脚步一定不能停止!

薪酬缩水,“裸辞”奋战25天三面美团,交叉面却被吊打,我太难了

Spring Cloud实战

薪酬缩水,“裸辞”奋战25天三面美团,交叉面却被吊打,我太难了

Spring Boot实战

薪酬缩水,“裸辞”奋战25天三面美团,交叉面却被吊打,我太难了

面试题整理(性能优化+微服务+并发编程+开源框架+分布式)

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
半,就从尾节点开始遍历,一直获取x的上一个节点

四、双端队列操作方法的源码剖析

offerFirst(E e) 方法,将指定的元素插入到此集合列表的前面,也就是将将元素添加到首部

源码剖析

/**

  • Inserts the specified element at the front of this list.

  • @param e the element to insert

  • @return {@code true} (as specified by {@link Deque#offerFirst})

  • @since 1.6

*/

分享

这次面试我也做了一些总结,确实还有很多要学的东西。相关面试题也做了整理,可以分享给大家,了解一下面试真题,想进大厂的或者想跳槽的小伙伴不妨好好利用时间来学习。学习的脚步一定不能停止!

[外链图片转存中…(img-CJzPQd4u-1713140742362)]

Spring Cloud实战

[外链图片转存中…(img-Bgkvg1Kv-1713140742362)]

Spring Boot实战

[外链图片转存中…(img-RstRE8k8-1713140742362)]

面试题整理(性能优化+微服务+并发编程+开源框架+分布式)

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-BbtcLxtU-1713140742364)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值