一、实现get方法

1、一般思维实现思路

  • 1)、将对象的值放入一个中间变量中。
  • 2)、遍历索引值,将中间量的下一个元素赋值给中间量。
  • 3)、返回中间量中的元素值。
  • 4)、示意图

  • get(2),传入角标,循环两次获取到[1]元素,如图.

2、实现思路实现

  • 1)、核心方法
  1. /**
  2. * 最基本的写法
  3. *
  4. * <p>按照角标循环元素,获取最后一个元素的值</p>
  5. *
  6. * <p>存在问题:效率不高</p>
  7. *
  8. * @param index 元素的角标
  9. * @return 角标代表的元素
  10. */
  11. public Object get(int index) {
  12. Node node = firstNode;
  13. for (int i = 0; i < index; i++) {
  14. node = node.next;
  15. }
  16. return node.elements;
  17. }
  • 2)、测试
  1. /**
  2. * @author liuyangos8888
  3. */
  4. public class TestGet {
  5. public static void main(String[] args) {
  6. testGetBase();
  7. }
  8. private static void testGetBase() {
  9. MyGetLinkedList001 myGetLinkedList001 = new MyGetLinkedList001();
  10. myGetLinkedList001.add("A");
  11. myGetLinkedList001.add("B");
  12. myGetLinkedList001.add("C");
  13. myGetLinkedList001.add("D");
  14. myGetLinkedList001.add("E");
  15. myGetLinkedList001.add("F");
  16. System.out.println(myGetLinkedList001.get(4));
  17. }
  18. }

3、思路缺少的条件

  • 1)、判断索引范围问题
  1. // 判断索引范围
  2. if (index < 0 || index > size - 1) {
  3. throw new RuntimeException("索引不合法!" + index);
  4. }
  • 2)、查找方式的优化问题(获取第888个索引时候,效率极低,建议使用折半查找)
  1. /**
  2. * 优化版,提高查找效率,折半判断
  3. *
  4. * @param index 索引角标
  5. * @return 索引对应的值
  6. */
  7. public Object get1(int index) {
  8. // 判断索引范围
  9. if (index < 0 || index > size - 1) {
  10. throw new RuntimeException("索引不合法!" + index);
  11. }
  12. Node node = null;
  13. // size>>1 除以2
  14. if (index <= (size >> 1)) {
  15. node = firstNode;
  16. for (int i = 0; i < index; i++) {
  17. node = node.next;
  18. }
  19. } else {
  20. node = lastNode;
  21. for (int i = size - 1; i > index; i--) {
  22. node = node.previous;
  23. }
  24. }
  25. return node.elements;
  26. }
  • 3)、发现的其他问题(采用此方式get时候,add需要size++)

  1. // 不做size++,在get判断范围的时候就会出现错误
  2. public void add(Object o) {
  3. Node node = new Node(o);
  4. if (firstNode == null) {
  5. firstNode = node;
  6. } else {
  7. node.previous = lastNode;
  8. node.next = null;
  9. lastNode.next = node;
  10. }
  11. lastNode = node;
  12. size++;
  13. }

二、实现remove方法

1、一般思维实现思路

  • 1)、把前一个元素的next元素,变为next.next元素
  • 2)、把最后一个元素的previous元素,变为previous.previous元素
  • 3)、size值减少操作
  • 4)、示意图

  • 有A、B、C三个节点,以链表形式存储(如上图)

  • 现在要删除节点B,A、C不变(如上图)

  • 方法就是截断A、C跟B的连接,A和C重新建立新的连接,JAVA的实现方式就是,用对象覆盖B节点的值。

2、实现思路实现

  • 1)、核心方法

  1. /**
  2. * 根据索引,删除数组中元素
  3. *
  4. * @param index 索引角标
  5. */
  6. public void remove(int index) {
  7. Node temp = getNode(index);
  8. if (temp != null) {
  9. Node up = temp.previous;
  10. Node down = temp.next;
  11. if (up != null) {
  12. up.next = down;
  13. }
  14. if (down != null) {
  15. down.previous = up;
  16. }
  17. size--;
  18. }
  19. }
  • 2)、测试
  1. /**
  2. * @author liuyangos8888
  3. */
  4. public class TestRemove {
  5. public static void main(String[] args) {
  6. MyGetLinkedList002 myGetLinkedList002 = new MyGetLinkedList002();
  7. myGetLinkedList002.add("A");
  8. myGetLinkedList002.add("B");
  9. myGetLinkedList002.add("C");
  10. myGetLinkedList002.add("D");
  11. myGetLinkedList002.add("E");
  12. myGetLinkedList002.add("F");
  13. System.out.println(myGetLinkedList002);
  14. myGetLinkedList002.remove(3);
  15. System.out.println(myGetLinkedList002);
  16. myGetLinkedList002.remove(0);
  17. System.out.println(myGetLinkedList002);
  18. myGetLinkedList002.remove(3);
  19. System.out.println(myGetLinkedList002);
  20. }
  21. }

3、思路缺少的条件

  • 1)、判断索引范围问题
  1. // 判断索引范围
  2. if (index < 0 || index > size - 1) {
  3. throw new RuntimeException("索引不合法!" + index);
  4. }
  • 2)、第一个元素删除和最后一个元素删除问题

  1. // 删除第一个元素的时候
  2. if (index == 0) {
  3. firstNode = down;
  4. }
  5. // 删除最后一个元素的时候
  6. if (index == size - 1) {
  7. lastNode = up;
  8. }

三、实现insert(add)方法

1、一般思维实现思路

  • 1)、获取索引的元素值,得到元素的前一个节点
  • 2)、把前节点的后一个节点设置为加入的节点
  • 3)、新节点的前一个节点设置为索引值节点的前节点
  • 4)、前一个节点的后一个节点是索引值节点
  • 5)、索引值节点的前一个节点是新节点
  • 6)、示意图

  • 有A、B、C三个节点,以链表形式存储,现在要添加D节点,到链表中.

  • 切断A、B的节点连接,A和D,D和B重新建立连接,生成新的链表

2、实现思路实现

  • 1)、核心方法
  • 方法一:

  1. /**
  2. * 插入一个元素在指定位置
  3. *
  4. * @param index 指定位置索引
  5. * @param o 插入的元素
  6. */
  7. public void insert(int index, Object o) {
  8. Node newNode = new Node(o);
  9. // 判断范围
  10. checkRound(index);
  11. Node temp = getNode(index);
  12. if (temp != null) {
  13. // 第一个元素和最后一个元素的时候
  14. if (index == 0 || index == size - 1) {
  15. temp.elements = newNode.elements;
  16. } else {
  17. Node up = temp.previous;
  18. isNull(up == null, "前一个元素为空");
  19. up.next = newNode;
  20. newNode.previous = up;
  21. newNode.next = temp;
  22. temp.previous = newNode;
  23. }
  24. }
  25. }
  • 方法二:

  1. /**
  2. * 插入一个元素在指定位置
  3. *
  4. * @param index 指定位置索引
  5. * @param o 插入的元素
  6. */
  7. public void insert1(int index, Object o) {
  8. Node newNode = new Node(o);
  9. // 判断范围
  10. checkRound(index);
  11. Node temp = getNode(index);
  12. if (temp != null) {
  13. // 第一个元素和最后一个元素的时候
  14. if (index == 0) {
  15. firstNode = newNode;
  16. newNode.next = temp.next;
  17. } else if (index == size - 1) {
  18. Node up = temp.previous;
  19. isNull(up == null, "前一个元素为空");
  20. up.next = newNode;
  21. newNode.previous = up;
  22. } else {
  23. Node up = temp.previous;
  24. isNull(up == null, "前一个元素为空");
  25. up.next = newNode;
  26. newNode.previous = up;
  27. newNode.next = temp;
  28. temp.previous = newNode;
  29. }
  30. }
  31. }
  • 2)、测试

  1. public class TestInsert {
  2. public static void main(String[] args) {
  3. MyInsertLinkedList003 myInsertLinkedList003 = new MyInsertLinkedList003();
  4. myInsertLinkedList003.add("A");
  5. myInsertLinkedList003.add("B");
  6. myInsertLinkedList003.add("C");
  7. myInsertLinkedList003.add("D");
  8. myInsertLinkedList003.add("E");
  9. myInsertLinkedList003.add("F");
  10. myInsertLinkedList003.insert1(1,"G");
  11. System.out.println(myInsertLinkedList003);
  12. }
  13. }

3、思路缺少的条件

  • 1)、判断索引范围问题

  1. private void checkRound(int index) {
  2. // 判断索引范围
  3. isNull(index < 0 || index > size - 1, "索引不合法!" + index);
  4. }
  5. /**
  6. * 判断空指针问题
  7. *
  8. * @param b 判断条件
  9. * @param string 抛出异常的原因
  10. */
  11. private void isNull(boolean b, String string) {
  12. if (b) {
  13. throw new RuntimeException(string);
  14. }
  15. }
  • 2)、第一个元素删除和最后一个元素删除问题

  1. if (temp != null) {
  2. // 第一个元素和最后一个元素的时候
  3. if (index == 0) {
  4. firstNode = newNode;
  5. newNode.next = temp.next;
  6. } else if (index == size - 1) {
  7. Node up = temp.previous;
  8. isNull(up == null, "前一个元素为空");
  9. up.next = newNode;
  10. newNode.previous = up;
  11. } else {
  12. Node up = temp.previous;
  13. isNull(up == null, "前一个元素为空");
  14. up.next = newNode;
  15. newNode.previous = up;
  16. newNode.next = temp;
  17. temp.previous = newNode;
  18. }
  19. }

四、实现泛型和全部代码


  1. package com.synway.test.collections.version3.finallist;
  2. import com.synway.test.collections.version3.basesimple.Node;
  3. /**
  4. * 最终手写版,添加泛型
  5. *
  6. * @author liuyangos8888
  7. */
  8. public class MyLinkedListFinal<T> {
  9. /**
  10. * 第一个节点
  11. */
  12. private Node firstNode;
  13. /**
  14. * 最后一个节点
  15. */
  16. private Node lastNode;
  17. /**
  18. * 链表大小
  19. */
  20. private int size;
  21. /**
  22. * 添加节点到数组中
  23. *
  24. * @param o 节点数据
  25. */
  26. public void add(T o) {
  27. Node node = new Node(o);
  28. if (firstNode == null) {
  29. firstNode = node;
  30. } else {
  31. node.previous = lastNode;
  32. node.next = null;
  33. lastNode.next = node;
  34. }
  35. lastNode = node;
  36. size++;
  37. }
  38. /**
  39. * 根据索引,删除数组中元素
  40. *
  41. * @param index 索引角标
  42. */
  43. public void remove(int index) {
  44. checkRound(index);
  45. Node temp = getNode(index);
  46. if (temp != null) {
  47. Node up = temp.previous;
  48. Node down = temp.next;
  49. if (up != null) {
  50. up.next = down;
  51. }
  52. if (down != null) {
  53. down.previous = up;
  54. }
  55. // 删除第一个元素的时候
  56. if (index == 0) {
  57. firstNode = down;
  58. }
  59. // 删除最后一个元素的时候
  60. if (index == size - 1) {
  61. lastNode = up;
  62. }
  63. size--;
  64. }
  65. }
  66. /**
  67. * 插入一个元素在指定位置
  68. *
  69. * @param index 指定位置索引
  70. * @param o 插入的元素
  71. */
  72. public void insert(int index, T o) {
  73. Node newNode = new Node(o);
  74. // 判断范围
  75. checkRound(index);
  76. Node temp = getNode(index);
  77. if (temp != null) {
  78. // 第一个元素和最后一个元素的时候
  79. if (index == 0) {
  80. firstNode = newNode;
  81. newNode.next = temp.next;
  82. } else if (index == size - 1) {
  83. Node up = temp.previous;
  84. isNull(up == null, "前一个元素为空");
  85. up.next = newNode;
  86. newNode.previous = up;
  87. } else {
  88. Node up = temp.previous;
  89. isNull(up == null, "前一个元素为空");
  90. up.next = newNode;
  91. newNode.previous = up;
  92. newNode.next = temp;
  93. temp.previous = newNode;
  94. }
  95. }
  96. }
  97. /**
  98. * 优化版,提高查找效率,折半判断
  99. *
  100. * @param index 索引角标
  101. * @return 索引对应的值
  102. */
  103. public T get(int index) {
  104. checkRound(index);
  105. Node node = getNode(index);
  106. return node != null ? (T) node.elements : null;
  107. }
  108. /**
  109. * 根据角标,获取节点
  110. *
  111. * @param index 传入角标
  112. * @return 获取节点
  113. */
  114. private Node getNode(int index) {
  115. Node node;
  116. // size>>1 除以2
  117. if (index <= (size >> 1)) {
  118. node = firstNode;
  119. for (int i = 0; i < index; i++) {
  120. node = node.next;
  121. }
  122. } else {
  123. node = lastNode;
  124. for (int i = size - 1; i > index; i--) {
  125. node = node.previous;
  126. }
  127. }
  128. return node;
  129. }
  130. /**
  131. * 审核传入的角标范围是否越界
  132. *
  133. * @param index 传入角标
  134. */
  135. private void checkRound(int index) {
  136. // 判断索引范围
  137. isNull(index < 0 || index > size - 1, "索引不合法!" + index);
  138. }
  139. /**
  140. * 判断空指针问题
  141. *
  142. * @param b 判断条件
  143. * @param string 抛出异常的原因
  144. */
  145. private void isNull(boolean b, String string) {
  146. if (b) {
  147. throw new RuntimeException(string);
  148. }
  149. }
  150. /**
  151. * 获取数组中元素
  152. *
  153. * @return 元素数组
  154. */
  155. @Override
  156. public String toString() {
  157. StringBuilder stringBuilder = new StringBuilder();
  158. stringBuilder.append("[");
  159. Node temp = firstNode;
  160. while (temp != null) {
  161. stringBuilder.append(temp.elements).append(",");
  162. temp = temp.next;
  163. }
  164. stringBuilder.setCharAt(stringBuilder.length() - 1, ']');
  165. return stringBuilder.toString();
  166. }
  167. }

JDK源码阅读-------自学笔记(二十四)(java.util.LinkedList 再探 自定义讲解)的更多相关文章

  1. JDK源码阅读-------自学笔记(二十五)(java.util.Vector 自定义讲解)

    Vector 向量 Vector简述 1).Vector底层是用数组实现的List 2).虽然线程安全,但是效率低,所以并不是安全就是好的 3).底层大量方法添加synchronized同步标记,sy ...

  2. JDK源码阅读-------自学笔记(一)(java.lang.Object重写toString源码)

    一.前景提要 Object类中定义有public String toString()方法,其返回值是 String 类型. 二.默认返回组成 类名+@+16进制的hashcode,当使用打印方法打印的 ...

  3. JDK源码阅读-------自学笔记(五)(浅析数组)

    一.数组基础 1.定义和特点 数组也可以看做是对象,数组变量属于引用类型,数组中每个元素相当于该队形的成员变量,数组对象存储在堆中. 2.初始化数组 常用类初始化 // 整型初始化 int[] int ...

  4. JDK源码阅读(1)_简介+ java.io

    1.简介 针对这一个版块,主要做一个java8的源码阅读笔记.会对一些在javaWeb中应用比较广泛的java包进行精读,附上注释.对于容易混淆的知识点给出相应的对比分析. 精读的源码顺序主要如下: ...

  5. element-ui Upload 上传组件源码分析整理笔记(十四)

    简单写了部分注释,upload-dragger.vue(拖拽上传时显示此组件).upload-list.vue(已上传文件列表)源码暂未添加多少注释,等有空再补充,先记下来... index.vue ...

  6. Java学习笔记二十四:Java中的Object类

    Java中的Object类 一:什么是Object类: Object类是所有类的父类,相当于所有类的老祖宗,如果一个类没有使用extends关键字明确标识继承另外一个类,那么这个类默认继承Object ...

  7. jdk源码阅读笔记-ArrayList

    一.ArrayList概述 首先我们来说一下ArrayList是什么?它解决了什么问题?ArrayList其实是一个数组,但是有区别于一般的数组,它是一个可以动态改变大小的动态数组.ArrayList ...

  8. JDK源码阅读(三):ArraryList源码解析

    今天来看一下ArrayList的源码 目录 介绍 继承结构 属性 构造方法 add方法 remove方法 修改方法 获取元素 size()方法 isEmpty方法 clear方法 循环数组 1.介绍 ...

  9. JDK源码阅读(一):Object源码分析

    最近经过某大佬的建议准备阅读一下JDK的源码来提升一下自己 所以开始写JDK源码分析的文章 阅读JDK版本为1.8 目录 Object结构图 构造器 equals 方法 getClass 方法 has ...

随机推荐

  1. 软件工程与UML作业2

    博客班级 https://edu.cnblogs.com/campus/fzzcxy/2018SE1 作业要求 https://edu.cnblogs.com/campus/fzzcxy/2018SE ...

  2. java注解(1)

    Java注解是附加在代码中的一些元信息,用于一些工具在编译.运行时进行解析和使用,起到说明.配置的功能.注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用.注解是Java SE5中引入的重要的语言 ...

  3. 解释器与JIT编译器

    解释器 JVM设计者们的初衷仅仅只是单纯地为了满足Java程序实现跨平台特性,因此避免采用静态编译的方式直接生成本地机器指令,从而诞生了实现解释器在运行时采用逐行解释字节码执行程序的想法. 解释器真正 ...

  4. kali一些基础工具

    目录 netcat netcat https://blog.csdn.net/fageweiketang/article/details/82833193 网络工具当中的瑞士军刀 -nc指令,nc可以 ...

  5. P4395 [BOI2003]Gem 气垫车

    树形dp 首先,我们可以考虑dp,把这个问题看成一个树的染色问题,用dp[i][j]表示以i为根节点,将树染成第i种颜色的最小代价,那么我们可以得到j的最大值是(log(maxn)/log(2)+1) ...

  6. Spring属性注入(set方式、构造函数方式、p名称空间、spel、复杂类型)

    1.set注入方式 (1)注入的为值类型(八大数据类型)的数据 配置文件: <?xml version="1.0" encoding="UTF-8"?&g ...

  7. iOS14剪切板探究,淘宝实现方法分析

    随着iOS 14的发布,剪切板的滥用也被大家所知晓.只要是APP读取剪切板内容,系统都会在顶部弹出提醒,而且这个提醒不能够关闭.这样,大家在使用APP的过程中就能够看到哪些APP使用了剪切板. 正好我 ...

  8. HTML+CSS系列:登录界面实现

    一.效果 二.具体实现 1.index.html <!DOCTYPE html> <html> <head> <meta charset="utf- ...

  9. Espruino似乎和Arduino一样

    参考:https://baike.baidu.com/item/Espruino Espruino 编辑 锁定 讨论   Espruino 是一个微处理器的 JavaScript 解释器,我们用它来创 ...

  10. JavaScript求数组中元素的最大值

    要求: 求数组[2,6,1,77,52,25,7]中的最大值. 实现思路: 声明一个保存最大元素的变量 max 默认最大值max定义为数组中的第一个元素arr[0] 遍历这个数组,把里面每个数组元素和 ...