Java实现链表结构

Java实现链表结构


    按链表的组织形式分有ArrayListLinkList两种。ArrayList内部其实是用数组的形式实现链表,比较适合链表大小确定或较少对链表进行增删操作的情况,同时对每个链表节点的访问时间都是constant

  而LinkList内部以一个List实现链表,比较适合需要频繁对链表进行操作的情况,对链表节点的访问时间与链表长度有关O(N)

    另外,根据实现形式可以分为直接式和使用Iterator(迭代模式)两种方法。使用Iterator时,需要实现

java.lang中的Iterable接口(或者也可以自己在链表内部定义自己的Iterator method

   
        使用迭代模式的优点:
           1,实现功能分离,简化容器接口。让容器只实现本身的基本功能,把迭代功能委托给外部类实现,符合类的设计原则。
           2,隐藏容器的实现细节。
           3为容器或其子容器提供了一个统一接口,一方面方便调用;另一方面使得调用者不必关注迭代器的实现细节。

           4,可以为容器或其子容器实现不同的迭代方法或多个迭代方法。

     5. 迭代效率高:Iterator 集合框架中的接口,主要用于迭代集合中的元素,而对于像LinkedList,如果你用For循环,则LinkedList的get()方法效率是非常低的,因为它每次访问一个元素都要从头开始。而对于Iterator来说,它就像你用吸管从饮料瓶吸饮料一样,每吸一口就消耗掉一点,它的迭代效率很高。

    我觉得第4点说的很好,对于一堆数据而言,不同的人(或业务逻辑)使用它的方式也不尽相同,定义一个theIterator继承Iterator,不仅提供nexthasNext 以及remove这个最小的操作集合,同时也可以提供更多的其它方法。在theIterator的实现类中,具体实现不同的迭代方法,比如顺序、逆序或根据其它语义进行遍历等,再通过类厂的方式将一个theIterator实现的对象交给用户使用。


首先是直接式实现链表的例子,这个例子只提供了几种链表操作的基本方法,仅用于示意:

  1. public class MyList<AnyType>  {  
  2.   
  3. private class Node<AnyType>{  
  4.   
  5. public  Node<AnyType> pre;  
  6.   
  7. public  Node<AnyType> next;  
  8.   
  9. public  AnyType      data;  
  10.   
  11. public Node(AnyType d, Node<AnyType>p, Node<AnyType> n){}  
  12.   
  13. public Node(){}  
  14.   
  15. }  
  16.   
  17. private int theSize;  
  18.   
  19. private Node<AnyType> Header;  
  20.   
  21. private Node<AnyType> Tail;  
  22.   
  23. public MyList(){}  
  24.   
  25. public void add(AnyType item){}  
  26.   
  27. public boolean isEmpty(){}  
  28.   
  29. public int size(){}  
  30.   
  31. public AnyType get( int idx){}  
  32.   
  33. public void print(){}  
  34.   
  35. }  

Node<AnyType>类定义了双向链表中节点的结构,它是一个私有类,而其属性和构造函数都是公有的,这样,其父类可以直接访问其属性,而外部类根本不知道Node类的存在。Data是节点中的数据,pre指向前一个Node节点,next指向后一个Node节点。其构造函数的实现如下,不解释:

[java] view plain copy
  1. public Node(AnyType d, Node<AnyType>p, Node<AnyType> n){  
  2.   
  3. this.data = d;  
  4.   
  5. this.pre = p;  
  6.   
  7. this.next = n;  
  8.   
  9. }  
  10.   
  11. public Node(){  
  12.   
  13. this.data = null;  
  14.   
  15. this.pre = null;  
  16.   
  17. this.next = null;  
  18.   
  19. }  



下面我们看一下链表的构造函数实现:

[java] view plain copy
  1. public MyList(){  
  2.   
  3. theSize = 0;  
  4.   
  5. Header = new Node<AnyType>(null,null,null);  
  6.   
  7. Tail   =  new Node<AnyType>(null,Header,null);  
  8.   
  9. Header.next = Tail;  
  10.   
  11. }  


我们构造了一个带有头、尾节点的双向链表,头节点的Next指向尾节点,为节点的pre指向头节点。链表长度起始为0

继续贴上链表类其它方法的实现,不解释了,应该比较清楚:


[java] view plain copy
  1. public void add(AnyType item){  
  2.   
  3.      Node<AnyType> aNode = new Node<AnyType>(item,null,null);  
  4.   
  5.      Tail.pre.next = aNode;  
  6.   
  7.      aNode.pre = Tail.pre;  
  8.   
  9.      aNode.next = Tail;  
  10.      
  11.      Tail.pre = aNode;  
  12.   
  13.      theSize++;  
  14.   
  15. }  
  16.   
  17. public boolean isEmpty(){  
  18.   
  19.      return ( theSize == 0);  
  20.   
  21. }  
  22.   
  23. public int size(){  
  24.   
  25.      return theSize;  
  26.   
  27. }  
  28.   
  29. public AnyType get( int idx){  
  30.   
  31.      if(idx > theSize-1 || idx < 0)  
  32.   
  33.            throw new IndexOutOfBoundsException();  
  34.   
  35.    
  36.   
  37.      Node<AnyType> current = new Node<AnyType>(null,Header,null);  
  38.   
  39.    
  40.   
  41.     for(int i = 0; i<idx; i++)  
  42.   
  43.        current = current.next;  
  44.   
  45.    return current.data;  
  46.   
  47. }  
  48.   
  49. public void print(){  
  50.   
  51.     Node<AnyType> current = Header.next;  
  52.   
  53. while(current.next != null){  
  54.    //如果AnyType是你自己定义 //的数据类型,那么请务必提供  
  55.    //一个toString方法,要么就不  
  56.    //要在链表里实现print方法。  
  57.   System.out.println(current.data.toString());   
  58.     
  59.   current = current.next;  
  60.   
  61. }  
  62.   
  63. }  


第二个例子是用迭代方法实现链表的例子,下面是类定义:

[java] view plain copy
  1. public class MyListItr<Type> implements Iterable<Type> {  
  2.   
  3. private class Node<Type>{  
  4.   
  5. public Type data;  
  6.   
  7. public Node<Type> pre;  
  8.   
  9. public Node<Type> next;  
  10.   
  11. public Node(){}  
  12.   
  13. public Node(Type d, Node<Type> p, Node<Type> n){}  
  14.   
  15. }  
  16.   
  17.    
  18.   
  19.    
  20.   
  21. private Node<Type> Header;  
  22.   
  23. private Node<Type> Tail;  
  24.   
  25. private int theSize;  
  26.   
  27.    
  28.   
  29. public MyListItr(){}  
  30.   
  31.    
  32.   
  33.    
  34.   
  35. public void add(Type item){}  
  36.   
  37.    
  38.   
  39. public void print(){}  
  40.   
  41.    
  42.   
  43. public int size(){}  
  44.   
  45. @Override  
  46.   
  47. public Iterator<Type> iterator() {}  
  48.   
  49.    
  50.   
  51. private class MyListIterator implements Iterator<Type>{  
  52.   
  53. @Override  
  54.   
  55. public boolean hasNext() {}  
  56.   
  57. @Override  
  58.   
  59. public Type next() {}  
  60.   
  61. @Override  
  62.   
  63. public void remove() {}  
  64.   
  65. }  
  66.   
  67. }  


  这里主要说一下与前面例子不同的地方:MyListItr类实现了java.lan.Itrable接口,因此我们必须实现其public Iterator<Type> iterator() {}方法,它的返回值是一个迭代器对象,那么我就定义一个内部私有类——MyListIterator,这个类实现了iterator接口。在public Iterator<Type> iterator() 方法中,我就构造这么一个MyListIterator的对象并返回。

    Iterator接口有三个方法是我们必须实现的,hasNextnextremove,这是迭代操作的最小集合了。对于不同的迭代器执行方式,我们可以定义多个类似MyListIterator这样的实现类,只要在链表类中的iterator() 方法中把具体实现类的对象返回给用户就OK了。

   罗嗦两句:我感觉如果我们定义链表类时,如果不继承Iterable接口,而是直接在类内部提供 public theIterator Iterator() 方法,theIterator是一个自定义接口,继承自Iterator接口,这样我们可以提供遵循theIterator接口的各种迭代方式的不同实现类,并通过一个类厂方法在链表的public theIterator Iterator() 方法中把具体迭代实现类的对象交给用户,以此就可以根据不同的需求,提供链表的多种迭代方式。我觉得这种方法是可行的,但并没有具体实验。

   下面只贴出链表iterator()方法和迭代实现类的MyListIterator代码,其它方法就不重复贴了:

[java] view plain copy
  1. @Override  
  2.   
  3. public Iterator<Type> iterator() {  
  4.   
  5. return new MyListIterator();  
  6.   
  7. }  
  8.   
  9. private class MyListIterator implements Iterator<Type>{  
  10.   
  11. Node<Type> current = Header.next;  
  12.   
  13. @Override  
  14.   
  15. public boolean hasNext() {  
  16.   
  17. return (current != Tail);  
  18.   
  19. }  
  20.   
  21. @Override  
  22.   
  23. public Type next() {  
  24.   
  25.   if(!hasNext())  
  26.   
  27.     throw new IndexOutOfBoundsException();    
  28.   
  29.   Type item = current.data;  
  30.   
  31.   current = current.next;  
  32.   
  33.   return item;  
  34. }  
  35.   
  36. @Override  
  37.   
  38. public void remove() {  
  39.   
  40.   if( !hasNext())  
  41.   
  42.     throw new NoSuchElementException();  
  43.   
  44.   current.pre.next = current.next;  
  45.   current.next.pre = current.pre;  
  46.   current = current.next;  
  47.   
  48.   theSize--;  
  49. }  
  50.   
  51. }  
参考博文:https://blog.csdn.net/wangkr111/article/details/7884322

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/559018.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot————快速搭建springboot项目

完成项目的创建信息 浏览器打开SPRING INITIALIZR网址&#xff1a; http://start.spring.io/ 如下图所示完成配置&#xff1a; 1.完成基础项目配置 2.相关名称 3.依赖jar包&#xff0c;如果是web项目&#xff0c;那么这里选择的Web依赖已经包含了开发web项目所必须的服务器…

史上最容易理解————GET和POST两种基本请求方法的区别

GET和POST两种基本请求方法的区别GET和POST是HTTP请求的两种基本方法&#xff0c;要说它们的区别&#xff0c;接触过WEB开发的人都能说出一二。最直观的区别就是GET把参数包含在URL中&#xff0c;POST通过request body传递参数。你可能自己写过无数个GET和POST请求&#xff0c;…

SpringBoot————JPA快速使用

本篇博客源码地址&#xff1a;https://github.com/DragonWatcher/ease-run 概述 Hibernate与JPA 本篇博客中的web项目选用Hibernate作为持久层框架。在Spring Boot中&#xff0c;我们需要了解另一个概念&#xff1a;JPA 上一句话可能有些歧义&#xff0c;并不是说JPA就是Sp…

MySQL优化建议汇总~~~

MySQL优化建议汇总~~~1、将经常要用到的字段&#xff08;比如经常要用这些字段来排序&#xff0c;或者用来做搜索&#xff09;&#xff0c;则最好将这些字段设为索引 2、字段的种类尽可能用int或者tiny int类型。另外字段尽可能用not null 3、当然无可避免某些字段会用到text&a…

Java泛型初探————泛型通配

T ? K V E?——表示不确定的Java类型 T——表示Java类型 K、V——分别代表键-值中的Key-Value&#xff0c;例如Map<K,V>&#xff0c;put(K key, V value) E——代表Element&#xff0c;例如List<E>&#xff0c;add(E e)java.lang.ClassClass类的实例…

谈谈 final、finally、 finalize 有什么不同?

谈谈 final、finally、 finalize 有什么不同&#xff1f;final 可以用来修饰类、方法、变量&#xff0c;分别有不同的意义&#xff0c;final 修饰的 class 代表不可以继承扩展&#xff0c;final 的变量是不可以修改的&#xff0c;而 final 的方法也是不可以重写的&#xff08;o…

Spring Boot————静态方法如何调用Spring容器中的Bean

问题分析 在使用静态方法的时候&#xff0c;某些情况下&#xff0c;需要使用类似自动注入的Bean来实现某些业务逻辑。 一般的非静态方法&#xff0c;可以很容易的通过在方法所在的类中Autowired自动将依赖的Bean注入到本类中&#xff0c;并操作。 静态方法在使用同样的操作流…

Java 集合Collection常见知识点汇总~

看了一些所谓大公司的JAVA面试问题&#xff0c;发现对于JAVA集合类的使用都比较看重似的&#xff0c;而自己在这方面还真的是所真甚少&#xff0c;抽空也学习学习吧。 java.util包中包含了一系列重要的集合类&#xff0c;而对于集合类&#xff0c;主要需要掌握的就是它的内部结…

内部类详解————匿名内部类

内部类三连击&#xff1a; 《内部类详解————匿名内部类》 《内部类详解————局部内部类》 《内部类详解————静态嵌套类》 应用场景 由于匿名内部类不利于代码的重用&#xff0c;因此&#xff0c;一般在确定此内部类只会使用一次时&#xff0c;才会使用匿名内部…

内部类详解————局部内部类

内部类三连击&#xff1a; 《内部类详解————匿名内部类》 《内部类详解————局部内部类》 《内部类详解————静态嵌套类》 定义 在方法或某个作用域内的内部类&#xff0c;称为局部内部类。匿名内部类就是一种局部内部类。 实现方式 public class OutterType …

关于面向对象以及三大特征的解释

关于面向对象以及三大特征的解释面向对象&#xff1a;在C语言编程中是面向过程而在Java编程中是面向对象的。面向过程更重要的是看重实现的具体过程&#xff0c;面向对象看重的是结果&#xff0c;不管其里面的具体过程&#xff0c;只看结果。举一个例子&#xff1a;我们从住的地…

内部类详解————静态内部类

内部类三连击&#xff1a; 《内部类详解————匿名内部类》 《内部类详解————局部内部类》 《内部类详解————静态内部类》 定义 静态内部类&#xff0c;又叫静态嵌套类或嵌套类。是使用static关键字修饰的内部类。 静态内部类可以用 private 修饰&#xff0c;这…

jvm gc垃圾回收机制和参数说明amp;amp;Java JVM 垃圾回收(GC 在什么时候,对什么东西,做了什么事情)

jvm gc&#xff08;垃圾回收机制&#xff09; Java JVM 垃圾回收&#xff08;GC 在什么时候&#xff0c;对什么东西&#xff0c;做了什么事情&#xff09; 前言&#xff1a;&#xff08;先大概了解一下整个过程&#xff09;作者&#xff1a;知乎用户 链接&#xff1a;https:…

【Mathematical Model】Ransac线性回归Python代码

Ransac算法&#xff0c;也称为随机抽样一致性算法&#xff0c;是一种迭代方法&#xff0c;用于从一组包含噪声或异常值的数据中估计数学模型。Ransac算法特别适用于线性回归问题&#xff0c;因为它能够处理包含异常值的数据集&#xff0c;并能够估计出最佳的线性模型。 1 简介 …

异常解析————Parameter metadata not available for the given statement

引言 在将数据存入mysql数据库时抛出异常&#xff1a;Parameter metadata not available for the given statement。参数元数据对于给定的声明不可用。 SQL本身并没有错误&#xff1a; Autowiredprivate JdbcTemplate jdbc;public Integer saveScenicSequence(ScenicSequence…

Java中HashMap的常用操作

Java中HashMap的常用操作HashMap<Integer, Integer> hashMap new HashMap<>();hashMap.put(5, 2);hashMap.put(9, 2);hashMap.put(8, 1);hashMap.put(7, 3);hashMap.put(16, 1);hashMap.put(10, 2);hashMap.put(6, 2);//其实下面两个键值对是没有存的hashMap.put(…

MySQL忽略主键冲突,避免重复插入数据的三种方式

方案一&#xff1a;ignore 插入时检索主键列表&#xff0c;如存在相同主键记录&#xff0c;不更改原纪录&#xff0c;只插入新的记录。 INSERT IGNORE INTO ignore关键字所修饰的SQL语句执行后&#xff0c;在遇到主键冲突时会返回一个0&#xff0c;代表并没有插入此条数据。…

SQL分页查询的介绍以及好处~~

SQL分页查询的介绍以及好处~~分页查询就是将过多的结果在有限的界面上分多页来显示&#xff0c;一般将分页查询分为两类&#xff1a; 逻辑分页、物理分页。 逻辑分页是在用户第一次访问时&#xff0c;将数据库的所有记录全部查询出来&#xff0c;添加到一个大集合中&#xff0c…

Swagger使用————接口参数注解的使用缺陷

问题描述 在使用springboot开发web项目时&#xff0c;用到了swagger框架&#xff0c;来生成web api文档。但是其中有一项是举例说明参数的结构&#xff0c;如下图&#xff1a;但是&#xff0c;这个功能真的是非常方便&#xff0c;因为可以让前端开发人员第一时间得知参数的内部…

分布式事务最终一致性常用方案

分布式事务最终一致性常用方案目前的应用系统&#xff0c;不管是企业级应用还是互联网应用&#xff0c;最终数据的一致性是每个应用系统都要面临的问题&#xff0c;随着分布式的逐渐普及&#xff0c;数据一致性更加艰难&#xff0c;但是也很难有银弹的解决方案&#xff0c;也并…