在写之前还是聊聊产生的背景吧,首先,在ArrayList某个位置添加数据的时候需要元素有后移的操作,在删除的时候也是需要元素的前移操作。除此之外,还有就是ArrayList这种集合的底层存储数据是一个数组,那么ArrayList的长度就不太好控制,在此时就有两种情况,一种是我并没有用完剩余的空间,还有一种就是我的数据不够长度,那么就需要扩容,这样一来,就还是会出现剩余的空间并没有使用完,造成空间的浪费。当然它的好处就是获取数据很方便。

在我们需要存储数据的时候, 最初的状态就是两种,一种是变量,一种是数组。在之前写了数组这种形式的集合去帮我们动态的扩容。现在解决刚刚说的问题,那么LinkedList就诞生了。

首先分析:

存储数据可以用变量,那么在Java中,变量有两大大类:1、八种计本类型   2、引用类型   那么在这里真正存储数据的时候就应该是使用引用类型。我们就可以得到下面这张图。

怎么才能让这些数据能够不这样游离呢?

使用链表,如下图(其中黑竖线右边的全是new关键字出来的,也就是一个个的对象。左边是用户看得到的引用)

基于此图,就得到了核心的代码如下。

private int size=0;//记录有效个数private Node<E> first;//记录头结点private Node<E> last;//记录尾结点/*** @author TQY 杨显伍* @date 2022/10/10 10:57* @param e 要添加的数据* @return boolean*  effect: 尾插法*/@Overridepublic boolean add(E e) {this.linkedLast(e);return true;}@Overridepublic E get(int index) {this.checkIndex(index);return node(index).item;}@Overridepublic E remove(int index) {this.checkIndex(index);return unLiked(node(index));}@Overridepublic int size() {return this.size;}@Overridepublic E[] toArray() {return this.copyArray();}@Overridepublic boolean addFirst(E addData) {this.linkedFirst(addData);return true;}@Overridepublic boolean add(int index, E addData) {this.checkIndex(index);//校验下标的合法性。if (index==0){//如果选择的是从头插入linkedFirst(addData);return true;}else if (index==(size-1)){//如果选择的是从尾部插入。linkedLast(addData);return true;}linkIndex(this.node(index),addData);return true;}/*** 根据下标替换成想要的元素。* @param index  下标* @param replaceData   需要替换的数据* @return   返回的是替换的数据*/@Overridepublic E replace(int index, E replaceData) {checkIndex(index);return linkNew(index,replaceData);}/*** @author TQY 杨显伍* @date 2022/10/11 11:07* @return boolean*  effect: 清空链表   思路1、先断next这条链,   就是从前往后循环的让头结点指向next并且然后将next置为空*                          然后断prev这条链,  就是从后往前循环的让尾结点指向prev并且将prev置为空*/public boolean clear(){Node<E> next=first.next;Node<E> prev=last.prev;for (int i = 0; i < size; i++) {first=next;next=null;}for (int i = size-1; i >=0 ; i--) {last=prev;prev=null;}size=0;return true;}/*** @author TQY 杨显伍* @date 2022/10/11 14:22* @return boolean*  effect: 思路二:从头结点一直向下都置为空。*/public boolean clearAll(){Node<E> remove=first;while (remove!=null){Node<E> next =remove.next;remove.item=null;remove.next=null;remove.prev=null;remove=next;}first = last = null;size=0;return true;}/*** @author TQY 杨显伍* @date 2022/10/11 14:48* @param index* @param addData* @return E[]*  effect: 在指定位置添加一个数组。*/@Overridepublic E[] add(int index, E... addData) {checkIndex(index);for (int i = 0; i < addData.length; i++) {linkNew(index,addData[i]);}size+=addData.length;return addData;}/*** @author TQY 杨显伍* @date 2022/10/11 14:54* @param addData*  @param method   插入的方式   head---头插法    last----尾插法* @return E[]*  effect: 让用户选择是进行头插法还是尾插法*/public E[] add(String method, E... addData) {linked(method, addData);return addData;}/*** @author TQY 杨显伍* @date 2022/10/10 11:00*  effect: 写私有方法的区域*//*** @author TQY 杨显伍* @date 2022/10/10 11:11* @param e*  effect: 思路:1、首先拿一个来记录最后一个节点*               2、再次创建一个新的游离的节点,在创建的时候就将新结点挂载在最后一个位置*               3、需要判断是不是第一个:*                    a.如果是第一个,那么记录第一个的first应该记录这个新结点*                    b.否则不是第一个,那么事先拿到的这个结点的记录下一个结点位置的就应该指向这个新结点。(此时拿到的这个结点就应该是在插入的时候的最后的那个结点)*               4、最后别忘了将记录有效长度增加一。*/private void linkedLast(E e){Node<E> l=last;  //记录尾结点Node<E> newNode=new Node(l,e,null);//既然是从尾部开始添加,那么就应该记录上一个的位置last=newNode;//尾结点指向新的结点if (l==null){//证明是空的。也就是第一次插入。first=newNode;}else {l.next=newNode;}this.size++;}/*** @author TQY 杨显伍* @date 2022/10/10 11:28* @param index*  effect: 判断下标是否合法*/private void checkIndex(int index){if (index<0){throw new RuntimeException("The subscript you entered is illegal ");}if (index>=size){throw new RuntimeException("The index you entered is out of range"+",Data index max is\t"+(size-1));}}/*** @author TQY 杨显伍* @date 2022/10/10 14:23* @param index* @return com.yxw.list.LinkedListBox.Node<E>*  effect: 根据下标获取元素*    思路:*    1、用一个空的结点来记录*         2、根据下标判断是否处于前半部分(index<(size>>1))*             a.如果下标处于前半部分,那么就将记录的结点指向头结点,然后从头依次的向后遍历,直至遍历至下标位置。然后返回记录的结点*             b.否则下标就处于后半部分,那么就将记录的结点指向尾结点,然后从尾依次的向前遍历,直至遍历至下标位置。然后返回记录的结点*/private Node<E> node(int index){Node<E> result;if (index<(size>>1)){//说明下标在前半部分result=first;for (int i = 0; i < index; i++) {result=result.next;}return result;}else {result=last;for (int i = size-1; i >index ; i--) {result=result.prev;}return result;}}/*** @author TQY 杨显伍* @date 2022/10/10 14:52* @param eNode* @return E*  effect: 删除元素*  思路:*  1、先获取一下删除的元素,以便将删除的元素返回给用户。*  2、在获取当前结点的下一个结点(next)和上一个结点(prev)*  3、判断是否为头结点*     a.如果是头结点,那么将头结点置为该结点的下一个结点,即就是在第二步接受的next置为头结点*     b.否则那就是中间的结点,那么就将上一个结点记录下一个结点的引用指向这个结点记录的下一个结点,然后将next置为null*  4、判断是否为尾结点:*     a.如果是尾结点,那么就将该结点的上一个结点指向尾结点。*     b.否则不是尾结点说明就是中间结点,那么就将该结点的下一个结点记录上一个结点的引用指向这个结点所记录的上一个结点,然后将perv置为空。*/private E unLiked(Node<E> eNode){Node<E> unNode=eNode;final Node<E> next = eNode.next;//当前节点的下一个节点的引用final Node<E> prev = eNode.prev;//当前节点的前一个节点的引用if (prev == null) {//证明现在是头节点first = next;//让下一个节点为头结点} else {//中间节点prev.next = next;//上一个的节点所存储的下一个节点的引用指向当前节点的下一个节点的引用eNode.prev = null;//当前指向上一个节点的引用置为空。}if (next == null) {//证明是尾结点last = prev;//将当前节点的上一个节点置为尾结点。} else {//中间节点next.prev = prev;//下一个节点所存储的上一个节点的信息指向上当前节点的上一个节点eNode.next = null;//当前节点指向下一个节点置为空。}size--;return unNode.item;}/*** @author TQY 杨显伍* @date 2022/10/10 16:22* @return E[]*  effect: 专门为转化为数组的方法所服务的*/private E[] copyArray(){E[] result=(E[]) new Object[size];for (int i = 0; i < size; i++) {result[i]= node(i).item;}return result;}/*** @author TQY 杨显伍* @date 2022/10/10 16:37*  effect: 现在已经能够保证插入的结点是在链表的中部进行插入的了*  那么现在的思路是:1、先获取当前位置所指向的下一个和上一个引用。*                 2、将新的结点的next指向当前结点,即就是newNode.next=node;*                 3、接着将新结点的prev指向当前结点的上一个结点的下一个结点。即就是newNode.prev=prev.next;*                 4、接着将当前结的perv指向新结点。即就是node.prev=node;*                 5、接着将当前的上一个结点的下一个指向结点指向新结点,也就是  node.prev.next=newNode;*/private void linkIndex(Node<E> node, E addData) {Node<E> prev =node.prev;Node<E> newNode=new Node<E>(null,addData,null);newNode.next=node;newNode.prev=prev;node.prev=newNode;node.prev.next=newNode;size++;}/*** @author TQY 杨显伍* @date 2022/10/10 16:12* @param addDate*  effect: 头插法,这个和尾插法刚好相反*/private void linkedFirst(E addDate){Node f=first;Node<E> newNode=new Node<E>(null,addDate,f);first=newNode;if (f.next==null){//说明这是一个空的,那么就last=newNode;}else {f.prev=newNode;}size++;}/*** @author TQY 杨显伍* @date 2022/10/11 10:26* @param index* @param e* @return E*  effect: 替换所在下标的元素。*           思路:*           1、先接收当前结点的所有的属性*           2、根据当前结点判断是否为头结点*               a.若是头结点,那么就将新结点置为头结点;然后将新结点的下一个结点置为next,最后将当前结点存储下一个结点的信息置为空*               b.否则则不是头结点,那么就先将新结点的下一个结点置为next,然后将当前结点的next置为空*           3、根据当前结点判断是否为尾结点*               a.若是尾结点,那么就将新结点置为尾结点,然后将新结点的上一个结点置为新结点的上一个结点,最后降当前结点的上一个结点置为空*               b.否则不是为结点,那么就将新结点的上一个结点置为prev,然后将当前结点的perv置为空*           4、最后将替换的结点的数据给返回给用户。*/private E linkNew(int index,E e){Node<E> oldNode=node(index);Node<E>  next=oldNode.next;Node<E>  prev=oldNode.prev;E result=oldNode.item;Node<E> newNode=new Node<>(null,e,null);if (prev==null){ //说明是头结点first=newNode;newNode.next=next;oldNode.next=null;}else {newNode.next=next;oldNode.next=null;}if (next==null){//说明是为结点last=newNode;newNode.prev=prev;oldNode.prev=null;}else {newNode.prev=prev;oldNode.prev=null;}return result;}private void linked(String method,E ...addData){if (method.equals("head")){for (int i = 0; i < addData.length; i++) {linkedFirst(addData[i]);}} else if (method.equals("last")){for (int i = 0; i < addData.length; i++) {linkedLast(addData[i]);}}else {throw new RuntimeException("Your Method has not exist");}size+=addData.length;}/*** @author TQY 杨显伍* @date 2022/10/10 10:54*  effect: 真正的节点类,保存真正的数据和上一个节点的地址,下一个地址*/private static class Node<E>{Node<E>  prev;E item;Node<E>  next;public Node (Node<E> prev,E item,Node<E> next){this.prev=prev;this.next=next;this.item=item;}}

最重要的就是对结点的控制。

在选用集合的时候,要考虑是读数据多还是写数据多,如果写数据多,那推荐使用LinkedList集合,否则推荐使用ArrayList。

模拟一个类似LinkedList的集合相关推荐

  1. Java LinkedList特有方法程序小解 使用LinkedList 模拟一个堆栈或者队列数据结构。...

    package Collection;import java.util.LinkedList;/* LinkedList:特有的方法 addFirst()/addLast(); getFirst()/ ...

  2. 使用LinkedList模拟一个堆栈或者队列数据结构

    使用LinkedList模拟一个堆栈或者队列数据结构. 堆栈:先进后出  如同一个杯子. 队列:先进先出  如同一个水管. import java.util.LinkedList;public cla ...

  3. python编写赛车游戏单机版_使用Python中OrderedDict模拟一个简单的竞速游戏排名

    上一篇,我们梳理了Python中关于字典排序的一些常用方法(杂乱无章的数据结构如何进行排序,简明讲述Python字典排序那些事).其中,我们讲到了Python的collections模块中的Order ...

  4. 模拟一个简单计算器_阅读模拟器的简单介绍

    模拟一个简单计算器 Read simulators are widely being used within the research community to create synthetic an ...

  5. 制作一个类似苹果VFL的格式化语言来描述UIStackView

    在项目中总是希望页面上各处的文字,颜色,字体大小甚至各个视图控件布局都能够在发版之后能够修改以弥补一些前期考虑不周,或者根据统计数据能够随时进行调整,当然是各个版本都能够统一变化.看到这样的要求后,第 ...

  6. 用Java模拟一个银行ATM系统

    用Java模拟一个银行ATM系统 系统功能介绍: 全部代码 示例截图 系统功能介绍: 使用面向对象的编程思想,尽可能模拟真实世界中的银行ATM业务流程. main方法里通过调用一行代码,完成整个业务流 ...

  7. Day22.List集合、ArrayList、LinkedList、集合工具类、泛型、通配符

    目录 List接口 ArrayList(最常用) LinkedList Collections 集合工具类 使用泛型 泛型(Generics) 自定义泛型 类型通配符 List接口 List接口存储元 ...

  8. 如何制作一个类似Tiny Wings的游戏(2) Cocos2d-x 2.1.4内含iOS版源代码

    在第二篇<如何制作一个类似Tiny Wings的游戏>基础上,增加添加主角,并且使用Box2D来模拟主角移动,原文<How To Create A Game Like Tiny Wi ...

  9. 如何制作一个类似Tiny Wings的游戏(2) Cocos2d-x 2.1.4

    在第二篇<如何制作一个类似Tiny Wings的游戏>基础上,增加添加主角,并且使用Box2D来模拟主角移动,原文<How To Create A Game Like Tiny Wi ...

最新文章

  1. [na]pc加入域认证细节
  2. 使用HTTP模块扩展 ASP.NET 处理
  3. java js关键字_js中的instanceof关键字
  4. http://blog.csdn.net/lovejavaydj/article/details/6
  5. pygame的学习以及python的巩固(窗口尺寸的显示)
  6. 【转】Unity3D将来时:IL2CPP(上)
  7. 使Struts2与Servlet并存解决办法 Filter转发Servlet
  8. javascript要点
  9. 面向对象基础及UML建模语言
  10. jquery对文本赋值和取值_jQuery对表单元素的取值和赋值操作代码
  11. JDBC的API与德鲁伊Druid连接池配置
  12. JVM到底怎么进行类加载器的呢?
  13. 网站遭遇DDoS***的解决方案
  14. 190728每日一句 不经一番寒彻骨 怎得梅花扑鼻香,一个人怎样保持激情去奋斗?
  15. Android中的Menu(菜单)的三种类型菜单的学习
  16. 华为OSN7500结构特点及产品定位相关知识
  17. 二、循环神经网络(RNN与LSTM)
  18. 光纤跳线接口_如何为10G SFP+光模块搭配对应的光纤跳线类型?
  19. 面试官:来写个代码求一下两个数的最大公约数吧
  20. C#按钮添加图片的解决

热门文章

  1. 简单明了的介绍了python面向对象编程
  2. S.H.E -《Forever新歌+精选》192Kbps[MP3!]
  3. 机器人开发--Universal Scene Description(USD)
  4. 经典算法大全51例——3.杨辉三角(又称帕斯卡三角形)
  5. 2022-08-17 工作记录--Git-贮藏 和 弹出贮藏
  6. 高阶统计量用于信号分析
  7. Yarn主导资源公平性调度算法
  8. 视频播放器是如何播放音视频的?
  9. 你怎样拼写计算机英语怎么说,你的名字是什么英文-“你的名字怎么拼写”用英语怎么说? – 手机爱问...
  10. jenkins linux 发邮件,Jenkins构建后发送邮件