这次牛逼了,面试字节被问LinkedList原理了!手足无措啊

概述

LinkedList底层是基于链表实现。链表没有长度限制,内存地址不需要固定长度,也不需要是连续的地址来进行存储,只需要通过引用来关联前后元素即可完成整个链表的连续。所以链表的优点就是添加删除元素比较快,只需要移动指针,并且不需要判断扩容。缺点就是因为没有索引,所以在查询和遍历元素时候比较慢。

使用场景:在增删操作使用较多,查询遍历操作使用较少情况下比较适合去使用;例如:拿来当栈使用。

数据结构

继承实现关系

1 public class LinkedList<E>
2     extends AbstractSequentialList<E>
3     implements List<E>, Deque<E>, Cloneable, java.io.Serializable
  • AbstractSequentialList:本质上面与继承AbstractList 没有什么区别,AbstractSequentialList完善了 AbstractList中没有实现的方法。
  • List:实现List接口。
  • Deque:实现Deque队列接口,拥有作为双端队列(队列和栈)的功能。
  • Cloneable:重写clone()方法,通过创建新的LinkedList 对象,遍历拷贝数据进行对象拷贝。
  • Serializable:重写read/writeObject() 方法实现序列化。

静态内部类

为什么Node这个类是静态的?答案是:这跟内存泄露有关,Node类是在LinkedList类中的,也就是一个内部类,若不使用static修饰,那么Node就是一个普通的内部类,在java中,一个普通内部类在实例化之后,默认会持有外部类的引用,这就有可能造成内存泄露(内部类与外部类生命周期不一致时)。但使用static修饰过的内部类(称为静态内部类),就不会有这种问题。【获取资料】

  • 非静态内部类会自动生成一个构造器依赖于外部类:也是内部类可以访问外部类的实例变量的原因。
  • 静态内部类不会生成,访问不了外部类的实例变量,只能访问类变量。
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;}}

基本属性

 // 元素数量
2     transient int size = 0;
3     // 头结点
4     transient Node<E> first;
5     // 尾节点
6     transient Node<E> last;

构造方法

1     public LinkedList() {
2     }
3 
4     public LinkedList(Collection<? extends E> c) {
5         this();
6         addAll(c);
7     }

主要方法解析

获取元素

peek():队列的查,获取队头元素。

public E get(int index) {    // 根据索引获取checkElementIndex(index);return node(index).item;}Node<E> node(int index) {// assert isElementIndex(index);// 利用二分法查找;小于中间数从头结点开始找,否则从尾节点开始找//加入Java开发交流君样:756584822一起吹水聊天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;}}// 获取队头元素 ,但是不删除队列的头元素(双端队列Deque中的方法)public E getFirst() {final Node<E> f = first;if (f == null)throw new NoSuchElementException();return f.item;}// 获取队尾元素,但是不删除队列的尾元素(实现双端队列Deque中的方法)public E getLast() {final Node<E> l = last;if (l == null)throw new NoSuchElementException();return l.item;}// 队列的查。获取队头元素。public E peek() {final Node<E> f = first;return (f == null) ? null : f.item;}

添加元素
  offer():队列的增,添加队尾元素,底层实现是调用add()->linkLast()。

push():栈的增,把元素压入栈中,添加对头元素,底层实现是调用addFirst()->linkFirst()。

 1     // 添加元素,默认在链表尾部添加2     public boolean add(E e) {3         linkLast(e);4         return true;5     }//加入Java开发交流君样:756584822一起吹水聊天6     // 指定索引添加元素7     public void add(int index, E element) {8         checkPositionIndex(index);  // 检验下标是否越界9 
10         if (index == size)  // 如果要插入的索引等于现有元素长度,说明是要在尾部插入元素
11             linkLast(element);
12         else    // 否则,获取该索引节点,在该节点之前插入元素
13             linkBefore(element, node(index));
14     }
15 
16     public boolean addAll(Collection<? extends E> c) {
17         return addAll(size, c);
18     }
19     public boolean addAll(int index, Collection<? extends E> c) {
20         checkPositionIndex(index);  // 检验下标是否越界
21 
22         Object[] a = c.toArray();
23         int numNew = a.length;
24         if (numNew == 0)
25             return false;
26 
27         // pred:前置节点,在该节点之后插入元素。succ:该索引位节点。
28         Node<E> pred, succ;
29         if (index == size) {
30             succ = null;
31             pred = last;
32         } else {
33             succ = node(index);
34             pred = succ.prev;
35         }
36         // 将数组设置为链表
37         for (Object o : a) {
38             @SuppressWarnings("unchecked") E e = (E) o;
39             Node<E> newNode = new Node<>(pred, e, null);    // 新建一个节点,指向前置节点
40             if (pred == null)   // 如果前置节点为空,说明该链表为空。将头节点指向当前节点
41                 first = newNode;
42             else    // 前置节点的后继节点指向当前节点
43                 pred.next = newNode;
44             pred = newNode; // 将当前节点设置为前置节点,供后面需要插入的节点使用
45         }//加入Java开发交流君样:756584822一起吹水聊天
46 
47         if (succ == null) {
48             last = pred;
49         } else {
50             pred.next = succ;
51             succ.prev = pred;
52         }
53 
54         size += numNew;
55         modCount++;
56         return true;
57     }
58 
59     // 添加元素到头结点(实现双端队列Deque中的方法)
60     public void addFirst(E e) {
61         linkFirst(e);
62     }
63     private void linkFirst(E e) {
64         final Node<E> f = first;    // 原头节点
65         final Node<E> newNode = new Node<>(null, e, f); // 新建一个节点,要添加的元素
66         first = newNode;    // 将头结点指向该新建的节点
67         if (f == null)  // 如果原头结点为空,说明原链表为空。这是添加的第一个元素,将尾结点也指向该新建的节点
68             last = newNode;
69         else    // 如果原头结点不为空,则将原头结点的前置节点指向该新建的节点
70             f.prev = newNode;
71         size++; // 元素数量+1
72         modCount++; // 修改次数+1
73     }
74     // 添加元素到尾结点(实现双端队列Deque中的方法)
75     public void addLast(E e) {
76         linkLast(e);
77     }
78     void linkLast(E e) {
79         final Node<E> l = last; // 原尾结点
80         final Node<E> newNode = new Node<>(l, e, null); // 新建一个节点,要添加的元素
81         last = newNode; // 将尾结点指向该新建的节点
82         if (l == null)  // 如果尾头结点为空,说明原链表为空。这是添加的第一个元素,将头结点也指向该新建的节点
83             first = newNode;
84         else    // 如果原尾结点不为空,则将原尾结点的后继节点指向该新建的节点
85             l.next = newNode;
86         size++; // 元素数量+1
87         modCount++; // 修改次数+1
88     }
89 
90     // 队列的添加方法
91     public boolean offer(E e) {
92         return add(e);
93     }
94 
95     // 栈的添加方法
96     public void push(E e) {
97         addFirst(e);
98     }

删除元素

poll():队列的删,获取对头元素并且对头元素删除。

pop():栈的删,返回的是栈顶元素并将栈顶元素删除。

 1     public boolean remove(Object o) {2         if (o == null) {3             for (Node<E> x = first; x != null; x = x.next) {4                 if (x.item == null) {5                     unlink(x);6                     return true;7                 }8             }9         } else {
10             for (Node<E> x = first; x != null; x = x.next) {
11                 if (o.equals(x.item)) {
12                     unlink(x);
13                     return true;
14                 }
15             }
16         }
17         return false;
18     }
19     public E remove(int index) {
20         checkElementIndex(index);
21         return unlink(node(index));
22     }
23     // 将该节点前置节点的下一个节点指向该节点后继节点,将该节点后继节点的上一个节点指向该节点前置节点。并将该节点置为空
24     E unlink(Node<E> x) {
25         // assert x != null;
26         final E element = x.item;
27         final Node<E> next = x.next;
28         final Node<E> prev = x.prev;
29 
30         if (prev == null) {
31             first = next;
32         } else {
33             prev.next = next;
34             x.prev = null;
35         }
36 
37         if (next == null) {
38             last = prev;
39         } else {
40             next.prev = prev;
41             x.next = null;
42         }
43 
44         x.item = null;
45         size--;
46         modCount++;
47         return element;
48     }
49     // 将头结点的下一个节点设置为新的头结点,并将原头节点置为空
50     private E unlinkFirst(Node<E> f) {
51         // assert f == first && f != null;
52         final E element = f.item;
53         final Node<E> next = f.next;
54         f.item = null;
55         f.next = null; // help GC
56         first = next;
57         if (next == null)
58             last = null;
59         else
60             next.prev = null;
61         size--;
62         modCount++;
63         return element;
64     }
65     //加入Java开发交流君样:756584822一起吹水聊天
66     // 将尾结点的上一个节点设置为新的尾结点,并将原尾节点置为空
67     private E unlinkLast(Node<E> l) {
68         // assert l == last && l != null;
69         final E element = l.item;
70         final Node<E> prev = l.prev;
71         l.item = null;
72         l.prev = null; // help GC
73         last = prev;
74         if (prev == null)
75             first = null;
76         else
77             prev.next = null;
78         size--;
79         modCount++;
80         return element;
81     }
82 
83     public E remove() {
84         return removeFirst();
85     }
86 
87     // 队列的删除方法
88     public E poll() {
89         final Node<E> f = first;
90         return (f == null) ? null : unlinkFirst(f);
91     }
92     // 栈的删除方法
93     public E pop() {
94         return removeFirst();
95     }

最后,祝大家早日学有所成,拿到满意offer

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/299664.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[WP8.1UI控件编程]Windows Phone自定义布局规则

3.2 自定义布局规则 上一节介绍了Windows Phone的系统布局面板和布局系统的相关原理&#xff0c;那么系统的布局面板并不一定会满足所有的你想要实现的布局规律&#xff0c;如果有一些特殊的布局规律&#xff0c;系统的布局面板是不支持&#xff0c;这时候就需要去自定义实现一…

聊聊编程语言的选择

我适合学什么编程语言呢&#xff1f;大家好&#xff0c;我是鱼皮&#xff0c;今天聊聊编程语言的选择问题&#xff0c;通过对 10 主流编程语言的特点、优劣、应用场景、发展前景等简单分析&#xff0c;希望帮还在迷茫的小伙伴们选择最适合自己的语言去学习。编程语言选择本文大…

IIS6文件权限不对触发了Windows身份认证问题解决方法

今天在iis上调试程序的时候突然发现需要登录: 通过csdn提问得知可能是权限设置有问题于是设置了下internet来宾用户: 结果问题没有解决.后来想想应该不是问题,因为我在自己机器上调试用的是Everyone权限,应该都可以访问. 于是我又去用户管理中重置internet来宾用户密码: 重置In…

C++ Exercises(十五)--排序算法的简单实现

structNode {//队列结点 int data; struct Node* pNext;};classCQueue{//队列类(带头结点&#xff09;public: CQueue(void); ~CQueue(void); bool isEmpty()const;//是否为空 void EnQueue(int num);//入队列 int DeQueue();//出队列 int Front()cons…

朋友圈终于能斗表情包了,会发表情包您就多发点!

全世界只有3.14 % 的人关注了青少年数学之旅今日&#xff0c;微信ios端更新至7.0.9版本&#xff0c;又上了一次微博热搜。版本新增了朋友圈图片评论功能&#xff0c;动态图、静态图均可&#xff0c;点击图片评论即可查看大图&#xff0c;评论图片同步聊天表情包库。各路网友收到…

EF Core 异步编程注意要点

????欢迎点赞 &#xff1a;???? 收藏 ⭐留言 ???? 如有错误敬请指正&#xff0c;赐人玫瑰&#xff0c;手留余香&#xff01;????本文作者&#xff1a;由webmote 原创&#xff0c;????作者格言&#xff1a;生活在于折腾&#xff0c;当你不折腾生活时&#x…

看电影的第一大禁忌 | 今日最佳

全世界只有3.14 % 的人关注了青少年数学之旅&#xff08;图源网络&#xff0c;侵权删&#xff09;

求职华为,被问观察者模式,从没有这种体验!!!

求职华为&#xff0c;被问观察者模式&#xff0c;从没有这种体验&#xff01;&#xff01;&#xff01;模式的定义与特点模式的结构与实现1. 模式的结构2. 模式的实现模式的应用实例模式的应用场景模式的扩展1. Observable类2. Observer 接口[ 观察者模式可以说是非常贴近我们…

SQL2005的配置

最近迷上c#&#xff0c;下午装好了SQL server management studio Express 附加经典的northwind数据库 然后用下面一段代码测试 1usingSystem;2usingSystem.Collections.Generic;3usingSystem.Text;4usingSystem.Data;5usingSystem.Data.Sql;6usingSystem.Data.SqlClient;78name…

为什么应该用record来定义DTO(续)

前言上次&#xff0c;我们介绍了因为DTO的“不变性”&#xff0c;应该用record来定义DTO。今天&#xff0c;我们来说明用record来定义DTO的另一个好处。问题首先&#xff0c;我们实现一个Controler&#xff0c;代码如下:[ApiController] [Route("[controller]")] pub…

资料分享 | 数学建模竞赛备战大全

全世界只有3.14 % 的人关注了青少年数学之旅目前针对数学建模的认知&#xff0c;绝大部分人还停留在数学建模竞赛阶段&#xff0c;并不知道数学建模是数据领域非常重要的一种方法。数学建模涉及的内容广泛&#xff0c;比如碎纸片问题中所涉及的图像识别及神经网络、小区开放问题…

初级Java开发工程师!绝密文档,面试手册全面突击!!!秋招已经到来

这里我要明说一下&#xff0c;不是Java初级和学习Java的千万不要乱看&#xff0c;否则~~~~ 你会怀疑人生&#xff0c;因为会浪费你时间啊&#xff01;&#xff01;&#xff01; 本次考点是Java初级开发工程师面试必备的一些东西!!! 1、数据类型 基本类型 byte/8、short/16、…

数学2600年,欧拉凭什么能当上“大王”?

全世界只有3.14 % 的人关注了青少年数学之旅何为数学&#xff1f;♠音乐家说&#xff0c;数学是世界上最和谐动听的音符♥植物学家说&#xff0c;世界上没有比数学更美的花朵♣美学家说&#xff0c;哪里有数学&#xff0c;哪里才有真正的美♦哲学家说&#xff0c;世界什么都在变…

NET流行高性能JSON框架-Json.NET

在日常编程中经常会使用到Json来进行数据的交互好在.Net平台下有很多开源的Json库使得我们能够比较轻松快速的处理各种复杂的Json&#xff0c;其中Newtonsoft库是NET的流行高性能JSON框架特性工具VS2010Newtonsoft库从NuGet下载合适的Newtonsoft.Json库1.在你需要引用Newtosoft…

Scribefire发CSDN博客

历史在非常久非常久曾经&#xff0c;CSDN是支持外部工具来写文章的&#xff0c;但是在还有一个非常久非常久曾经就不行了。突然看到CSDN有能够用外部工具来写博客了&#xff08;CSDN的公告&#xff09;&#xff0c;一直以来都纠结这个问题&#xff0c;CSDN的编辑器不好用&#…

今日笔记!——分析Java应用性能

1 问题描述 因产品架构的复杂性&#xff0c;可能会导致性能问题的因素有很多。根据部署架构&#xff0c;大致的可以分为应用端瓶颈、数据库端瓶颈、环境瓶颈三大类。可以根据瓶颈的不同部位&#xff0c;选择相应的跟踪工具进行跟踪分析。 应用层面瓶颈大致有如下两类&#xf…

除了PS,还有它可以轻松实现图像处理!

全世界只有3.14 % 的人关注了青少年数学之旅在我们生活中&#xff0c;常见的图像处理软件有Adobe Photoshop、Adobe Illustrator等。然而&#xff0c;并非只有软件才能实现图像处理&#xff0c;通过编程手段也是能实现的&#xff01;今天&#xff0c;小天将要带着大家走进计算机…

微服务并不能解决你的烂代码问题

点击上方蓝字关注我们“微服务并不能解决你的烂代码问题很久以来&#xff0c;软件的交付质量一直是一个大家比较关心的问题&#xff0c;而程序员和架构师也一直在极力寻找一种更好的方式来构建应用系统。随着互联网爆炸式的增长&#xff0c;对于系统的交付速度和质量的要求也日…

protobuf在java应用中通过反射动态创建对象

2019独角兽企业重金招聘Python工程师标准>>> ---恢复内容开始--- 最近编写一个游戏用到protobuf数据格式进行前后台传输&#xff0c;苦于protobuf接受客户端的数据时是需要数据类型的如xxx.parseForm(...),这样就要求服务器在接受客户端请求时必须知道客户端传递的数…

黑科技轮胎:有能发电的,脑洞简直不要太大...

全世界只有3.14 % 的人关注了青少年数学之旅人类历史上的很多伟大发明&#xff0c;都是由脑洞产生的&#xff0c;这样那样&#xff0c;然后问题就解决了&#xff0c;过程很复杂&#xff0c;却又很简单&#xff0c;甚至有时候&#xff0c;是一种很奇葩的方式。在漫长的历史进程中…