模拟LinkedList
Linkedlist是一个集合容器,具有增删改查基本功能,本例中模拟增删查,对于修改,只需要将原节点处的val更新为新值即可,增加和删除在链表中速度是比较快的,查找,修改慢,因为要从头或者从尾开始遍历。自定义的linkedList名称为yLinkedList。
类似于ArrayList中需要有一个数组来存储元素,在Linkedlist中也需要一个容器来存储元素,这个容器叫什么名字自己随意取,这里使用Node。该容器理论上可以放在任意处,只要在Mylinkedlist中可以调用即可,但考虑到外部类不会直接使用Node,所以将Node作为Mylinkedlist的内部私有类比较合适。
链表有长度,所以具有属性size,链表通过索引查找元素时,从头部或者尾部开始遍历,链表中需要存在头部(head)和尾部(tail)节点,LinkedList是双链表,在每个Node节点中都需要存在一个指向前一个节点指针和指向后一个节点指针,且Node节点也必须保存当前节点的值,这样MyLinkedList的大致结构就有了,由于是简单模拟增删查,List接口中的方法挺多的,故未实现List
package com.demo.langdemo.jdk; public class MyLinkedList {
int size;
// 当前集合的首节点
private Node head;
// 当前集合的尾节点
private Node tail; /*
* 存储元素的容器
*/
private class Node {
// 当前节点的值
Object val;
// 当前节点的前一个节点
Node pre;
// 当前节点的后一个节点
Node next; Node(Node pre, Object val, Node next) {
this.pre = pre;
this.val = val;
this.next = next;
}
}
}
在首部添加元素
当前添加的元素的前一个节点是null,后一个节点就是之前的head节点,若当前的首节点为null,则表明整个集合元素个数为0,,将当前元素添加后,首尾节点相同,都为当前节点。当首节点不为null,则将当前首节点的的pre指向新添加节点,并且将首节点指向新添加节点
public void addFirst(Object val) {
// 定位新添加的节点的位置,添加到首节点,则其前驱节点为null
Node newNode = new Node(null, val, head);
// 修改其他节点位置
if (head == null) {
// 首节点为空,则说明当前集合中还不存在数据,尾节点为空,也说明为空,但两个判断条件同样效果,使用一个判断即可
tail = newNode;
} else {
head.pre = newNode;
}
// 更新head为当前节点
head = newNode;
size++;
}
在尾部添加元素
在尾部添加元素时,前一个节点即为尾节点,后一个节点为null,当尾节点为null时,则表明整个集合元素个数为0,此时首尾节点相同,都为当前节点。当尾节点不为null时,将当前尾节点的的next指向新添加节点,并且将tail节点指向新添加元素
public void addLast(Object val) {
// 定位新添加的节点的位置,添加到尾节点,则其后继节点为null
Node newNode = new Node(head, val, null);
if (tail == null) {
// 尾节点为空
head = newNode;
} else {
tail.next = newNode;
} tail = newNode;
size++;
}
直接调用add方法时,从头插入或者从尾插入可根据具体情况选择
在指定位置添加元素
首先确定索引是否超出了当前集合的size,超出则抛出异常,如果index==size,则在尾部插入,如果index==0,则在头部插入,否则即是在中间部位插入。中间部位插入时,首先要获取到查入位置已存在的节点(查询方法在后面介绍),获取其pre节点,新添加节点的pre指向已存在节点的pre节点,新添加节点的next节点指向已经存在的已存在的节点。已存在的节点的pre节点的next节点指向新添加节点,已存在的节点的pre节点指向新添加节点
public void add(int index, Object val) {
// 需要找到当前索引的节点
try {
if (index < 0 || index > size) {
throw new Exception("error");
}
} catch (Exception e) {
e.printStackTrace();
}
if (index == size) {
addLast(val);
} else if (index == 0) {
addFirst(val);
} else {
Node currNode = getNode(index);
Node pre = currNode.pre;
Node newNode = new Node(pre, val, currNode);
pre.next = newNode;
currNode.pre = newNode;
size ++;
}
}
查找指定位置的节点
从中间开始查找无从下手,但每次添加元素时,size都会做对应的自增,所以每个索引也是唯一对应一个元素的,这里不考虑性能,直接就从head开始遍历查找,找index对应元素,那么index-1的next节点即为当前要找的index对应的元素
public Object get(int index) {
return getNode(index).val;
} private Node getNode(int index) {
Node node = head;
for (int i = 0; i < index; i++) {
// 将head的next节点重新赋值给node,继续查询下一个head的next节点,这样,index-1的next节点即为我们所需要的节点
node = head.next;
}
return node;
}
根据指定索引删除
获取索引对应的元素,找到其pre和next节点,并将pre和next节点之间建立联系,将当前被删除元素的pre和next节点置为null,解除与集合中元素的联系
public Object remove(int index) {
// 找到索引对应的节点
Node node = getNode(index);
// 获取该节点的pre和next节点
Node pre = node.pre;
Node next = node.next; // 设置该节点的pre和next对应关系
pre.next = next;
next.pre = pre; node.next=null;
node.pre = null; size--;
return node.val;
}
根据元素值删除
从头或者从尾或者两头开始遍历等,扎到每个node节点的值与要删除的对比,若一致,则删除,将当前要删除的节点的pre和next节点删除,解除与集合中元素的关系,注意要删除的元素必须是重写了equals方法,否则删除的结果可能与预期不一致。
public Object remove(Object val) {
Node node = head;
for (int i = 0; i < size; i++) {
if (node.val.equals(val)) {
Node pre = node.pre;
Node next = node.next; pre.next = next;
next.pre = pre;
break;
}
node = node.next;
} node.pre = null;
node.next = null;
size--;
return node.val;
}
完整实现类
MyLinkedList.java
package com.demo.langdemo.jdk; public class MyLinkedList {
int size;
// 当前集合的首节点
private Node head;
// 当前集合的尾节点
private Node tail; /*
* 存储元素的容器
*/
private class Node {
// 当前节点的值
Object val;
// 当前节点的前一个节点
Node pre;
// 当前节点的后一个节点
Node next; Node(Node pre, Object val, Node next) {
this.pre = pre;
this.val = val;
this.next = next;
}
} public void addLast(Object val) {
// 定位新添加的节点的位置,添加到尾节点,则其后继节点为null
Node newNode = new Node(head, val, null);
if (tail == null) {
// 尾节点为空
head = newNode;
} else {
tail.next = newNode;
} tail = newNode;
size++;
} public void addFirst(Object val) {
// 定位新添加的节点的位置,添加到首节点,则其前驱节点为null
Node newNode = new Node(null, val, head);
// 修改其他节点位置
if (head == null) {
// 首节点为空,则说明当前集合中还不存在数据,尾节点为空,也说明为空,但两个判断条件同样效果,使用一个判断即可
tail = newNode;
} else {
head.pre = newNode;
}
// 更新head为当前节点
head = newNode;
size++;
} public void add(Object val) {
// addFirst(val);
addLast(val);
} public Object get(int index) {
return getNode(index).val;
} private Node getNode(int index) {
Node node = head;
for (int i = 0; i < index; i++) {
// 将head的next节点重新赋值给node,继续查询下一个head的next节点,这样,index-1的next节点即为我们所需要的节点
node = head.next;
}
return node;
} public String toString() {
StringBuilder str = new StringBuilder(); Node node = head;
while (node != null) {
str.append(node.val).append(",");
node = node.next;
} return str.deleteCharAt(str.length() - 1).toString();
} public void add(int index, Object val) {
// 需要找到当前索引的节点
try {
if (index < 0 || index > size) {
throw new Exception("error");
}
} catch (Exception e) {
e.printStackTrace();
}
if (index == size) {
addLast(val);
} else if (index == 0) {
addFirst(val);
} else {
Node currNode = getNode(index);
Node pre = currNode.pre;
Node newNode = new Node(pre, val, currNode);
pre.next = newNode;
currNode.pre = newNode;
size++;
}
} public Object remove(int index) {
// 找到索引对应的节点
Node node = getNode(index);
// 获取该节点的pre和next节点
Node pre = node.pre;
Node next = node.next; // 设置该节点的pre和next对应关系
pre.next = next;
next.pre = pre; node.next = null;
node.pre = null; size--;
return node.val;
} public Object remove(Object val) {
Node node = head;
for (int i = 0; i < size; i++) {
if (node.val.equals(val)) {
Node pre = node.pre;
Node next = node.next; pre.next = next;
next.pre = pre;
break;
}
node = node.next;
} node.pre = null;
node.next = null;
size--;
return node.val;
} public int getSize() {
return size;
} }
在本次模拟中,未考虑性能等问题,只是实现了简单的功能,了解下链表的结构及实现思路。还需要查看大神写的LinkedList源码,了解其实现精髓。
ArrayList和LinkedList,前者查询和修改快,添加和删除慢,后者是查询,修改慢,添加删除快。
模拟LinkedList的更多相关文章
- JAVA容器-模拟LinkedList实现(双链表)
概述 LinkedList实质上就是双向链表的拓展的实现,我们将关注一下问题.LinkedList 1.双向链表怎么来实现插入.删除.查询? 2.利用二分法提高查询效率. 3.不同步,线程不安全,需要 ...
- 单元测试(Spring)
单元测试是指对软件中的最小可测试单元进行的检查和验证,是软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试. 单元测试好处:提高代码质量(实现功能.逻辑 ...
- 框架Mockito
一.什么是mock测试,什么是mock对象? 先来看看下面这个示例: 从上图可以看出如果我们要对A进行测试,那么就要先把整个依赖树构建出来,也就是BCDE的实例. 一种替代方案就是使用mocks 从图 ...
- 大数据之路week02 List集合的子类
1:List集合的子类(掌握) (1)List的子类特点 ArrayList: 底层数据结构是数组,查询快,增删慢. 线程不安全,效率高. Vector: 底层数据结构是数组,查询快,增删慢. 线程安 ...
- C++模拟实现JDK中的ArrayList和LinkedList
Java实现ArrayList和LinkedList的方式采用的是数组和链表.以下是用C++代码的模拟: 声明Collection接口: #ifndef COLLECTION_H_ #define C ...
- 使用LinkedList模拟一个堆栈或者队列数据结构
使用LinkedList模拟一个堆栈或者队列数据结构. 堆栈:先进后出 如同一个杯子. 队列:先进先出 如同一个水管. import java.util.LinkedList; public cl ...
- java 16 - 5 LinkedList模拟栈数据结构的集合
请用LinkedList模拟栈数据结构的集合,并测试 题目的意思是: 你自己的定义一个集合类,在这个集合类内部可以使用LinkedList模拟. package cn_LinkedList; impo ...
- Java LinkedList特有方法程序小解 && 使用LinkedList 模拟一个堆栈或者队列数据结构。
package Collection; import java.util.LinkedList; /* LinkedList:特有的方法 addFirst()/addLast(); getFirst( ...
- 面试题:使用LinkedList来模拟一个堆栈或者队列数据结构
请使用LinkedList来模拟一个堆栈或者队列数据结构. 堆栈:先进后出 First In Last Out (FILO) 队列:先进先出 First In First Out (FIFO) 我 ...
随机推荐
- Ubuntu下编译boost for Android
下载https://github.com/moritz-wundke/Boost-for-Android 解压后进入目录 运行 ./build-android.sh $(NDK_ROOT) NDK_R ...
- window, linux, mac 比较文件和文件夹的区别
windows 端 winmerge beyondcompare Mac 和 linux 端 Meld kdiff3 diff command 更多可参考:https://alternativeto ...
- MySql学习笔记【二、库相关操作】
命令规范 关键字.函数名称大写 数据库.表名.字段名小写 语句须以分号结尾 切换使用数据库 USE database_name 如:USE test 查看数据库列表 SHOW {DATABASES|S ...
- Tensorflow模型代码调试问题
背景: 不知道大家有没有这样的烦恼:在使用Tensorflow搭建好模型调试的过程中,经常会碰到一些问题,当时花了不少时间把这个问题解决了,一段时间后,又出现了同样的问题,却怎么也不记得之前是怎么解决 ...
- Linux Shell Web超级终端工具shellinabox
Shell是Linux内核应用程序,是指“为使用者提供操作界面”的软件,也是命令解析器,它类似于Windows操作系统DOS下的cmd.exe应用程序.它接收用户命令,然后调用相应的应用程序,用户一般 ...
- STM32定义变量位于指定的SRAM地址
1.定义一个数组比如value[],让数组的首地址指向特定的SRAM地址,比如0x20000100 1)__align(8) uint8_t value[20] __attribute__((at(0 ...
- linux基础—课堂随笔_03 SHELL脚本编程基础
shell脚本编程基础 条件选择:if语句 选择执行: 注意:if语句可嵌套 单分支 if(开头)判断条件:then条件为真的分支代码 fi(结尾) 双分支 if(开头)判断条件:then条件为真的分 ...
- cubase 音频的淡入淡出
- 浅入深出Vue:登录
上一篇我们实现了注册功能,现在我们来实现一下登录功能. 准备工作 新建登录组件 添加登录组件的路由对象 新建登录组件 Login.vue: <template> <div> & ...
- IO框架:asyncio 上篇
如何定义/创建协程 只要在一个函数前面加上 async 关键字,这个函数对象是一个协程,通过isinstance函数,它确实是Coroutine类型. from collections.abc imp ...