当前位置: 首页 > news >正文

LinkedList源码解析

添加元素方法

add方法详解

/*E:创建LinkedList对象时泛型中设置好的数据类型e: 传递的参数boolean: 添加成功即为true,添加失败即为false
*/public boolean add(E e) {// 在链表最后一个结点后面插入新结点(根据待插入元素封装的结点)   linkLast(e);// 返回true,代表添加成功return true;}void linkLast(E e) {// 声明一个新结点,将尾节点赋给lfinal Node<E> l = last;// 将数据e封装为一个新结点,用于挂载到链表上,三个参数l代表新结点的前一个结点,e新结点的数据域,null即新结点的后继结点final Node<E> newNode = new Node<>(l, e, null);// 新插入的结点会变成新的尾结点last = newNode;// 如果原来的尾结点为null,代表此前链表中一个结点都没有,此刻心插入结点又是链表的首结点if (l == null)first = newNode;// 原来链表中存在结点时,新插入的结点会是原来尾结点l的后继结点elsel.next = newNode;// 插入成功后,链表中结点/元素的数量+1size++;modCount++;}

结论: 通过插入过程,我们发现LinkedList是一个双向链表,因为数据会被封装成结点,进而挂载到双向链表

push方法详解

/*** 添加元素* 无返回值,参数为程序员调用push方法传递的值*/public void push(E e) {addFirst(e);}public void addFirst(E e) {linkFirst(e);}private void linkFirst(E e) {// 声明一个结点f,将首结点赋给f结点final Node<E> f = first;// 将待添加数据封装成一个新结点,此时null为新结点的前驱结点,e为数据域,f为后继结点final Node<E> newNode = new Node<>(null, e, f);// 更新首结点first = newNode;// 如果f结点为null,代表之前首结点为null,即链表中没有结点,此刻新插入的结点也是尾结点if (f == null)last = newNode;// 否则代表之前链表中有结点,将之前的首结点f的前驱结点指向新结点elsef.prev = newNode;// 添加后元素个数+1size++;modCount++;}

offer方法详解

// offer方法本质上就是add方法
public boolean offer(E e) {return add(e);
}

addAll方法详解


/***  index:待添加元素的位置索引*	c:待添加的数据集*/
public boolean addAll(int index, Collection<? extends E> c) {// 检测索引的合法性,当不合法时底层代码会抛出异常,此时程序结束checkPositionIndex(index);// 将数据集转换为Object类型的数组Object[] a = c.toArray();// 获取到数据集的长度int numNew = a.length;// 如果长度为0,证明你要添加的数据集是空集合,不需要添加到链表中去,返回falseif (numNew == 0)return false;Node<E> pred, succ;// 如果索引为size,代表将集合c追加到链表中if (index == size) {// 后继结点设置为nullsucc = null;// 当前链表的尾节点设置为前驱结点pred = last;} else {// 将集合c插入到链表中// 获取当前链表的index位置对应的结点 赋给succsucc = node(index);// 在获取succ的前驱结点赋给predpred = succ.prev;}// 遍历整个a集合for (Object o : a) {@SuppressWarnings("unchecked") E e = (E) o;// 将数据o封装为新结点,新结点的前驱结点即为pred,// e即为数据域,null是当前结点的后继结点Node<E> newNode = new Node<>(pred, e, null);// 如果前驱结点为null,证明链表中此时没有结点if (pred == null)// 新结点即为我们的首结点first = newNode;// 证明链表中此时有结点时else// pred的后继结点指向新结点pred.next = newNode; // pred更新为newNodepred = newNode;}// 如果succ为null,代表我们是以追加的形式存放集合c中if (succ == null) {// 此时pred就是我们新链表的尾结点last = pred;} else {// 代表随机插入的形式存放数据集c到链表,此时原来链表中index对应的结点成了pred的// 后继结点,prev本身是集合c中的最后一个元素封装成的结点pred.next = succ;//原来链表中index对应的结点succ的前驱结点指向了集合c中的最后一个元素封装成的结点succ.prev = pred;}// 元素个数+	集合c中元素的个数 代表此刻LinkedList中的元素个数size += numNew;modCount++;// 返回true,添加成功return true;}/*** 检测索引是否合法*/private void checkPositionIndex(int index) {// 如果isPositionIndex方法返回的是false,证明索引不合法,则抛出索引越界异常if (!isPositionIndex(index))throw new IndexOutOfBoundsException(outOfBoundsMsg(index));}private boolean isPositionIndex(int index) {// 当索引>=0且索引<=size此时 我们是合法的索引 返回true,否则返回falsereturn index >= 0 && index <= size;}/*** 获取index 位置对应的结点*/Node<E> node(int index) {// 如果位置信息 小于 链表中元素个数的一半,此时从前往后遍历if (index < (size >> 1)) {// 获取首结点作为当前结点Node<E> x = first;// 从首结点开始向后遍历,index次for (int i = 0; i < index; i++)// 当前结点的后继结点赋给当前结点x = x.next;// 出来循环后即找到index位置的结点,将其返回即可return x;} else {//  此时代表从后向前遍历// 将尾节点作为当前结点Node<E> x = last;for (int i = size - 1; i > index; i--)当前结点往前移动一位    x = x.prev;// 返回index位置所对应的结点return x;}/*由此可以证明index参数本质不是索引的概念,因为索引不需要一个个的遍历知识点拓展:索引必须在物理内存上是连续的,而链表物理内存上并不连续,只是逻辑上连续而已*/}

set方法详解

/*** index:我要修改的结点位置(不是索引,链表中本质是没有索引的概念的)* element:要修改成的新元素*/public E set(int index, E element) {// 检测位置信息是否合法checkElementIndex(index);// 获取到index对应的结点Node<E> x = node(index);// 获取要被修改结点的数据域E oldVal = x.item;// 更新当前结点的数据域x.item = element;// 返回旧值return oldVal;
}

get方法详解

/*** index:我查询的位置信息对应的数据,即链表中第index+1结点对应的数据*/public E get(int index) {// 检测位置信息的合法性checkElementIndex(index);// 位置信息合法,获取该位置对应的node的数据域,并返回return node(index).item;
}

contains代码详解:

/*** 查询集合中是否包含元素o,包含则返回true,不包含则返回false*/
public boolean contains(Object o) {// 如果元素o在链表中的位置信息为-1,说明o并不存在,否则o包含在链表之中return indexOf(o) != -1;
}/*** 获取元素o对应的位置信息,o在集合中存在则返回对应的位置信息,不存在返回-1*/
public int indexOf(Object o) {// 初始化 位置信息的值为0int index = 0;// 当o为nullif (o == null) {// 从链表的首结点开始遍历,一直到尾节点for (Node<E> x = first; x != null; x = x.next) {// 如果当前结点的数据域为null,则说明找到我想要的结点了if (x.item == null)// 返回当前结点对应的位置信息return index;// 没找到时index+1index++;}} else {// 从链表的首结点开始遍历,一直到尾节点for (Node<E> x = first; x != null; x = x.next) {// 如果当前结点的数据域为null,则说明找到我想要的结点了if (o.equals(x.item))return index;// 没找到时index+1index++;}}// 整个链表都没找到,那么返回-1return -1;
}

clear方法详解

/*** 清理整个链表中的数据*/public void clear() {// 遍历整个链表,x作为当前结点,初始化为首结点for (Node<E> x = first; x != null; ) {// 获取到当前结点的下一个结点Node<E> next = x.next;// 将当前结点的数据域、后继结点、前驱结点全设置为空,为了方便GCx.item = null;x.next = null;x.prev = null;// 将next结点设置为当前要处理的结点x = next;}// 将first last结点设置nullfirst = last = null;// 链表中元素个数回复为0size = 0;modCount++;}

相关文章:

  • 嵌入式硬件篇---麦克纳姆轮(简单运动实现)
  • 【金仓数据库征文】国产数据库KingbaseES安装与使用详解
  • 深度解析 MySQL 与 Spring Boot 长耗时进程:从故障现象到根治方案(含 Tomcat 重启必要性分析)
  • Java与Go语言对比教程
  • LeetCode --- 448 周赛
  • MCP项目实例 - client sever交互
  • AZScreenRecorder最新版:功能强大、操作简便的手机录屏软件
  • LangGraph(三)——添加记忆
  • 《算法导论(第4版)》阅读笔记:p17-p27
  • 事务连接池
  • Linux笔记---System V共享内存
  • 海市蜃楼的形成原理
  • JVM之内存管理(二)
  • vue 组件函数式调用实战:以身份验证弹窗为例
  • 数字相机的快门结构
  • 互联网大厂Java求职面试:基于RAG的智能问答系统设计与实现-3
  • 4.6java异常处理
  • 每日算法刷题Day2 5.10:leetcode数组1道题3种解法,用时40min
  • Java零组件实现配置热更新
  • C++发起Https连接请求
  • 气象干旱黄色预警继续:陕西西南部、河南西南部等地特旱
  • “拼好假”的年轻人,今年有哪些旅游新玩法?
  • 黑灰产工作室为境外诈骗集团养号引流,冒充美女与男性裸聊后敲诈勒索
  • 代理销售保险存在误导行为,农业银行重庆市分行相关负责人被罚款0.1万元
  • Meta正为AI眼镜开发人脸识别功能
  • 抗战回望21︱《“良民”日记》:一个“良民”在沦陷区的见闻与感受