JDK源码阅读-------自学笔记(二十四)(java.util.LinkedList 再探 自定义讲解)
一、实现get方法
1、一般思维实现思路
- 1)、将对象的值放入一个中间变量中。
- 2)、遍历索引值,将中间量的下一个元素赋值给中间量。
- 3)、返回中间量中的元素值。
- 4)、示意图
- get(2),传入角标,循环两次获取到[1]元素,如图.
2、实现思路实现
- 1)、核心方法
/**
* 最基本的写法
*
* <p>按照角标循环元素,获取最后一个元素的值</p>
*
* <p>存在问题:效率不高</p>
*
* @param index 元素的角标
* @return 角标代表的元素
*/
public Object get(int index) {
Node node = firstNode;
for (int i = 0; i < index; i++) {
node = node.next;
}
return node.elements;
}
- 2)、测试
/**
* @author liuyangos8888
*/
public class TestGet {
public static void main(String[] args) {
testGetBase();
}
private static void testGetBase() {
MyGetLinkedList001 myGetLinkedList001 = new MyGetLinkedList001();
myGetLinkedList001.add("A");
myGetLinkedList001.add("B");
myGetLinkedList001.add("C");
myGetLinkedList001.add("D");
myGetLinkedList001.add("E");
myGetLinkedList001.add("F");
System.out.println(myGetLinkedList001.get(4));
}
}
3、思路缺少的条件
- 1)、判断索引范围问题
// 判断索引范围
if (index < 0 || index > size - 1) {
throw new RuntimeException("索引不合法!" + index);
}
- 2)、查找方式的优化问题(获取第888个索引时候,效率极低,建议使用折半查找)
/**
* 优化版,提高查找效率,折半判断
*
* @param index 索引角标
* @return 索引对应的值
*/
public Object get1(int index) {
// 判断索引范围
if (index < 0 || index > size - 1) {
throw new RuntimeException("索引不合法!" + index);
}
Node node = null;
// size>>1 除以2
if (index <= (size >> 1)) {
node = firstNode;
for (int i = 0; i < index; i++) {
node = node.next;
}
} else {
node = lastNode;
for (int i = size - 1; i > index; i--) {
node = node.previous;
}
}
return node.elements;
}
- 3)、发现的其他问题(采用此方式get时候,add需要size++)
// 不做size++,在get判断范围的时候就会出现错误
public void add(Object o) {
Node node = new Node(o);
if (firstNode == null) {
firstNode = node;
} else {
node.previous = lastNode;
node.next = null;
lastNode.next = node;
}
lastNode = node;
size++;
}
二、实现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)、核心方法
/**
* 根据索引,删除数组中元素
*
* @param index 索引角标
*/
public void remove(int index) {
Node temp = getNode(index);
if (temp != null) {
Node up = temp.previous;
Node down = temp.next;
if (up != null) {
up.next = down;
}
if (down != null) {
down.previous = up;
}
size--;
}
}
- 2)、测试
/**
* @author liuyangos8888
*/
public class TestRemove {
public static void main(String[] args) {
MyGetLinkedList002 myGetLinkedList002 = new MyGetLinkedList002();
myGetLinkedList002.add("A");
myGetLinkedList002.add("B");
myGetLinkedList002.add("C");
myGetLinkedList002.add("D");
myGetLinkedList002.add("E");
myGetLinkedList002.add("F");
System.out.println(myGetLinkedList002);
myGetLinkedList002.remove(3);
System.out.println(myGetLinkedList002);
myGetLinkedList002.remove(0);
System.out.println(myGetLinkedList002);
myGetLinkedList002.remove(3);
System.out.println(myGetLinkedList002);
}
}
3、思路缺少的条件
- 1)、判断索引范围问题
// 判断索引范围
if (index < 0 || index > size - 1) {
throw new RuntimeException("索引不合法!" + index);
}
- 2)、第一个元素删除和最后一个元素删除问题
// 删除第一个元素的时候
if (index == 0) {
firstNode = down;
}
// 删除最后一个元素的时候
if (index == size - 1) {
lastNode = up;
}
三、实现insert(add)方法
1、一般思维实现思路
- 1)、获取索引的元素值,得到元素的前一个节点
- 2)、把前节点的后一个节点设置为加入的节点
- 3)、新节点的前一个节点设置为索引值节点的前节点
- 4)、前一个节点的后一个节点是索引值节点
- 5)、索引值节点的前一个节点是新节点
- 6)、示意图
- 有A、B、C三个节点,以链表形式存储,现在要添加D节点,到链表中.
- 切断A、B的节点连接,A和D,D和B重新建立连接,生成新的链表
2、实现思路实现
- 1)、核心方法
- 方法一:
/**
* 插入一个元素在指定位置
*
* @param index 指定位置索引
* @param o 插入的元素
*/
public void insert(int index, Object o) {
Node newNode = new Node(o);
// 判断范围
checkRound(index);
Node temp = getNode(index);
if (temp != null) {
// 第一个元素和最后一个元素的时候
if (index == 0 || index == size - 1) {
temp.elements = newNode.elements;
} else {
Node up = temp.previous;
isNull(up == null, "前一个元素为空");
up.next = newNode;
newNode.previous = up;
newNode.next = temp;
temp.previous = newNode;
}
}
}
- 方法二:
/**
* 插入一个元素在指定位置
*
* @param index 指定位置索引
* @param o 插入的元素
*/
public void insert1(int index, Object o) {
Node newNode = new Node(o);
// 判断范围
checkRound(index);
Node temp = getNode(index);
if (temp != null) {
// 第一个元素和最后一个元素的时候
if (index == 0) {
firstNode = newNode;
newNode.next = temp.next;
} else if (index == size - 1) {
Node up = temp.previous;
isNull(up == null, "前一个元素为空");
up.next = newNode;
newNode.previous = up;
} else {
Node up = temp.previous;
isNull(up == null, "前一个元素为空");
up.next = newNode;
newNode.previous = up;
newNode.next = temp;
temp.previous = newNode;
}
}
}
- 2)、测试
public class TestInsert {
public static void main(String[] args) {
MyInsertLinkedList003 myInsertLinkedList003 = new MyInsertLinkedList003();
myInsertLinkedList003.add("A");
myInsertLinkedList003.add("B");
myInsertLinkedList003.add("C");
myInsertLinkedList003.add("D");
myInsertLinkedList003.add("E");
myInsertLinkedList003.add("F");
myInsertLinkedList003.insert1(1,"G");
System.out.println(myInsertLinkedList003);
}
}
3、思路缺少的条件
- 1)、判断索引范围问题
private void checkRound(int index) {
// 判断索引范围
isNull(index < 0 || index > size - 1, "索引不合法!" + index);
}
/**
* 判断空指针问题
*
* @param b 判断条件
* @param string 抛出异常的原因
*/
private void isNull(boolean b, String string) {
if (b) {
throw new RuntimeException(string);
}
}
- 2)、第一个元素删除和最后一个元素删除问题
if (temp != null) {
// 第一个元素和最后一个元素的时候
if (index == 0) {
firstNode = newNode;
newNode.next = temp.next;
} else if (index == size - 1) {
Node up = temp.previous;
isNull(up == null, "前一个元素为空");
up.next = newNode;
newNode.previous = up;
} else {
Node up = temp.previous;
isNull(up == null, "前一个元素为空");
up.next = newNode;
newNode.previous = up;
newNode.next = temp;
temp.previous = newNode;
}
}
四、实现泛型和全部代码
package com.synway.test.collections.version3.finallist;
import com.synway.test.collections.version3.basesimple.Node;
/**
* 最终手写版,添加泛型
*
* @author liuyangos8888
*/
public class MyLinkedListFinal<T> {
/**
* 第一个节点
*/
private Node firstNode;
/**
* 最后一个节点
*/
private Node lastNode;
/**
* 链表大小
*/
private int size;
/**
* 添加节点到数组中
*
* @param o 节点数据
*/
public void add(T o) {
Node node = new Node(o);
if (firstNode == null) {
firstNode = node;
} else {
node.previous = lastNode;
node.next = null;
lastNode.next = node;
}
lastNode = node;
size++;
}
/**
* 根据索引,删除数组中元素
*
* @param index 索引角标
*/
public void remove(int index) {
checkRound(index);
Node temp = getNode(index);
if (temp != null) {
Node up = temp.previous;
Node down = temp.next;
if (up != null) {
up.next = down;
}
if (down != null) {
down.previous = up;
}
// 删除第一个元素的时候
if (index == 0) {
firstNode = down;
}
// 删除最后一个元素的时候
if (index == size - 1) {
lastNode = up;
}
size--;
}
}
/**
* 插入一个元素在指定位置
*
* @param index 指定位置索引
* @param o 插入的元素
*/
public void insert(int index, T o) {
Node newNode = new Node(o);
// 判断范围
checkRound(index);
Node temp = getNode(index);
if (temp != null) {
// 第一个元素和最后一个元素的时候
if (index == 0) {
firstNode = newNode;
newNode.next = temp.next;
} else if (index == size - 1) {
Node up = temp.previous;
isNull(up == null, "前一个元素为空");
up.next = newNode;
newNode.previous = up;
} else {
Node up = temp.previous;
isNull(up == null, "前一个元素为空");
up.next = newNode;
newNode.previous = up;
newNode.next = temp;
temp.previous = newNode;
}
}
}
/**
* 优化版,提高查找效率,折半判断
*
* @param index 索引角标
* @return 索引对应的值
*/
public T get(int index) {
checkRound(index);
Node node = getNode(index);
return node != null ? (T) node.elements : null;
}
/**
* 根据角标,获取节点
*
* @param index 传入角标
* @return 获取节点
*/
private Node getNode(int index) {
Node node;
// size>>1 除以2
if (index <= (size >> 1)) {
node = firstNode;
for (int i = 0; i < index; i++) {
node = node.next;
}
} else {
node = lastNode;
for (int i = size - 1; i > index; i--) {
node = node.previous;
}
}
return node;
}
/**
* 审核传入的角标范围是否越界
*
* @param index 传入角标
*/
private void checkRound(int index) {
// 判断索引范围
isNull(index < 0 || index > size - 1, "索引不合法!" + index);
}
/**
* 判断空指针问题
*
* @param b 判断条件
* @param string 抛出异常的原因
*/
private void isNull(boolean b, String string) {
if (b) {
throw new RuntimeException(string);
}
}
/**
* 获取数组中元素
*
* @return 元素数组
*/
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("[");
Node temp = firstNode;
while (temp != null) {
stringBuilder.append(temp.elements).append(",");
temp = temp.next;
}
stringBuilder.setCharAt(stringBuilder.length() - 1, ']');
return stringBuilder.toString();
}
}
JDK源码阅读-------自学笔记(二十四)(java.util.LinkedList 再探 自定义讲解)的更多相关文章
- JDK源码阅读-------自学笔记(二十五)(java.util.Vector 自定义讲解)
Vector 向量 Vector简述 1).Vector底层是用数组实现的List 2).虽然线程安全,但是效率低,所以并不是安全就是好的 3).底层大量方法添加synchronized同步标记,sy ...
- JDK源码阅读-------自学笔记(一)(java.lang.Object重写toString源码)
一.前景提要 Object类中定义有public String toString()方法,其返回值是 String 类型. 二.默认返回组成 类名+@+16进制的hashcode,当使用打印方法打印的 ...
- JDK源码阅读-------自学笔记(五)(浅析数组)
一.数组基础 1.定义和特点 数组也可以看做是对象,数组变量属于引用类型,数组中每个元素相当于该队形的成员变量,数组对象存储在堆中. 2.初始化数组 常用类初始化 // 整型初始化 int[] int ...
- JDK源码阅读(1)_简介+ java.io
1.简介 针对这一个版块,主要做一个java8的源码阅读笔记.会对一些在javaWeb中应用比较广泛的java包进行精读,附上注释.对于容易混淆的知识点给出相应的对比分析. 精读的源码顺序主要如下: ...
- element-ui Upload 上传组件源码分析整理笔记(十四)
简单写了部分注释,upload-dragger.vue(拖拽上传时显示此组件).upload-list.vue(已上传文件列表)源码暂未添加多少注释,等有空再补充,先记下来... index.vue ...
- Java学习笔记二十四:Java中的Object类
Java中的Object类 一:什么是Object类: Object类是所有类的父类,相当于所有类的老祖宗,如果一个类没有使用extends关键字明确标识继承另外一个类,那么这个类默认继承Object ...
- jdk源码阅读笔记-ArrayList
一.ArrayList概述 首先我们来说一下ArrayList是什么?它解决了什么问题?ArrayList其实是一个数组,但是有区别于一般的数组,它是一个可以动态改变大小的动态数组.ArrayList ...
- JDK源码阅读(三):ArraryList源码解析
今天来看一下ArrayList的源码 目录 介绍 继承结构 属性 构造方法 add方法 remove方法 修改方法 获取元素 size()方法 isEmpty方法 clear方法 循环数组 1.介绍 ...
- JDK源码阅读(一):Object源码分析
最近经过某大佬的建议准备阅读一下JDK的源码来提升一下自己 所以开始写JDK源码分析的文章 阅读JDK版本为1.8 目录 Object结构图 构造器 equals 方法 getClass 方法 has ...
随机推荐
- 使用phoenix连接hbase
hbase本身不支持SQL查询,为了实现这个功能,引入了phoenix,通过它可以实现hbase的sql查询.这里记录下如何配置并使用phoenix来操作hbase. 1. 下载地址 phoenix下 ...
- Springboot定时任务@Scheduled注解形式,参数详解
参数详解 1.占位符 1 秒 是 0-59 , - * / 2 分 是 0-59 , - * / 3 时 是 0-23 , - * / 4 日 是 1-31 , - * ? / L W 5 月 是 1 ...
- WebGL之延迟着色
什么是延迟着色(Deferred Shading)?它是相对于正常使用的正向着色(Forward Shading)而言的,正向着色的工作模式:遍历光源,获取光照条件,接着遍历物体,获取物体的几何数据, ...
- Git+Gitlab+Ansible剧本实现一键部署动态网站(5)
项目前言 之前已经写了一篇关于git和ansible的博客<Git+Gitlab+Ansible剧本实现一键部署Nginx–技术流ken>.关于git,gitliab,ansible在我以 ...
- java8的::
public static void main(String[] args) throws Exception { // 第一种方法引用的类型是构造器引用,语法是Class::new,或者更一般的形式 ...
- 华为云推UGO:一手抓结构迁移,一手抓SQL转换
摘要:UGO,一款专注于异构数据库对象迁移和应用迁移的专业化工具. 近日,华为云推出数据库和应用迁移 UGO,这是一款专注于异构数据库对象迁移和应用迁移的专业化工具,通过预迁移评估.结构迁移两大核心功 ...
- vue入门(一)----工程vue_sell
1.利用vue脚手架工具创建vue,webpack工程目录 npm install -g vue-cli 安装vue脚手架 vue init webpack sell 初始化一个webpack模板, ...
- 从零搭建Spring Boot脚手架(7):Elasticsearch应该独立服务
1. Spring Data Elasticsearch Spring Data Elasticsearch是Spring Data项目的子项目,提供了Elasticsearch与Spring的集成. ...
- C++练习案例1.计算机类(利用多态实现)
c++简单计算机类 简介 大家好,这里是天天like的博客,这是我发的第一篇随笔,用来记录我的学习日程,大家可以相互学习,多多交流,感谢 今天我要记录的随笔是在学习c++多态的知识点练习改进的一个案例 ...
- Kafka和RocketMQ底层存储之那些你不知道的事
大家好,我是yes. 我们都知道 RocketMQ 和 Kafka 消息都是存在磁盘中的,那为什么消息存磁盘读写还可以这么快?有没有做了什么优化?都是存磁盘它们两者的实现之间有什么区别么?各自有什么优 ...