一,迭代器的基本知识:

1,为什么要用迭代器?(迭代:即对每一个元素进行一次“问候”)

比如说,我们定义了一个ADT(抽象数据类型),作为ADT的一种实现,如单链表。而单链表的基本操作中,大部分需要用到依次遍历单链表中的每一个元素。一般而言,我们就是用for循环来实现遍历,这样,当你新增一个对单链表的操作并需要使用遍历时,你就得重新写一个for循环而实现遍历。那么,为什么不将迭代(遍历)作为一种基本的ADT操作(基本的ADT操作如:新增一个元素、删除一个元素)呢?于是,迭代器就出场了。

2,鉴于上述迭代器的用途,我们可以知道:迭代器是用来遍历元素的,那么说明这个数据结构应该有一组“相同”类型的元素(如,单链表、数组……)。因此,在JAVA中,为什么在学习集合(Collection)时,经常会有迭代器(Iterator)概念。

3,JAVA中实现迭代器的方式有几种?

主要有两种:a,定义一个公共类并 implements Iterator<T>接口;同时,该类与实现ADT的类(如本例中的 LinkListWithIterator<T>)分开。当然,这两个类还需要以某种方式交互。因为,迭代器需要知道我迭代的东东到底是谁嘛。下面代码粗糙描述了公共的实现迭代器的类:SeparateIterator<T>如何与实现ADT的类交互。

 public Interface ListInterface<T>{
     //此处定义了链表的基本操作
 }

 public class LinkList<T> implements ListInterface<T>{
     //LinkList类实现了ADT
     //此处实现链表的基本操作
 }
 public class SeparateIterator<T> implements Iterator<T>
 {
     //此处实现了Iterator接口中的方法
 }

 //将实现了ADT类(即,LinkList)的对象作为构造函数的参数传入到实现迭代器类的构造方法中
 //从而实现它们之间的交互
 Iterator<T> it = new SeparateIterator<T>(LinkList实例);

b,在实现ADT的类中定义一个私有内部类,该私有内部类实现迭代器的功能,使用私有内部类的好处是,在该私有内部类中可以直接访问待迭代元素的数据域(具体看下面代码中的注释),效率较高。以下例子就是这种方式。

4,如何为自定义的数据结构实现一个迭代器?

在JAVA类库中java.util.ArrayList<T> 类代表存储一组“相同”类型的数据元素的数组,ArrayList类从其父类AbstractList中继承了 iterator() 方法来返回一个可以对存放在ArrayList中的元素进行迭代的迭代器。那万一根据需要,需要自定义了一个数据结构,又如何给该数据结构实现迭代器呢?下面以单链表为例进行说明:

二,具体的实例的代码

1,定义一个ListInterface<T>接口(相当于一个ADT),指定了单链表中的各种基本操作:

 public interface ListInterface<T> {
     //返回当前链表中元素个数
     public int length();

     ////获得索引为index处的元素值,注意:索引从0开始
     public T get(int index);

     //返回链表中指定元素的索引,若指定元素不存在,返回-1
     public int locate(T element);

     //在指定索引index处添加指定的element
     public void insert(T element, int index);

     //采用尾插法为链表增加新节点
     public void add(T element);

     //采用头插法为链表增加新节点
     public void addAtHeader(T element);

     //删除指定索引处的节点
     public T delete(int index);

     //删除线性表中的最后一个元素
     public T remove();

     //清空线性表
     public void clear();
 }

2,定义ListWithIteratorInterface<T>接口并 extends ListInterface<T>,这样就可以将迭代器与ADT类联系起来。因为,实现ADT的类LinkListWithIterator<T> implements ListWithIteratorInterface<T>,从而以ListWithIteratorInterface为中界,将LinkListWithIterator(带有迭代器的ADT)与ListInterface<T>(线性表)联系起来了。进而,使得LinkListWithIterator<T>是一个:带有迭代器的单链表了。

这样,就可以通过①LinkListWithIterator<T>的对象调用 getIterator() 获得一个迭代器,②而该迭代器类又是LinkListWithIterator<T>的内部类,即可以直接迭代LinkListWithIterator的数据域。由①②,LinkListWithIterator<T>就表示一个带有迭代器的单链表了。

 import java.util.Iterator;

 public interface ListWithIteratorInterface<T> extends ListInterface<T>{
     public Iterator<T> getIterator();
 }

3,定义了一个类LinkListWithIterator<T>实现了ListInterface<T>接口(相当于ADT的实现类),LinkListWithIterator<T>中定义了一个内部类:IteratorForLinkedList,该内部类 implements Iterator<T>,

 import java.util.Iterator;
 import java.util.NoSuchElementException;

 public class LinkListWithIterator<T> implements ListWithIteratorInterface<T>{
     private class Node{
         private T data;
         private Node next;
         private Node(){

         }
         private Node(T data, Node next){
             this.data = data;
             this.next = next;
         }
     }
     /*
      * 该内部类用来实现迭代器,从而能够对单链表进行迭代
      * 使用内部类好处:能够直接访问ADT(抽象数据类型,如此例中的链表)的数据域,迭代效率高
      */
     private class IteratorForLinkedList implements Iterator<T>{

         private Node nextNode;//Node类型是通过私有内部类定义的表示链表结点的一种数据类型
         private IteratorForLinkedList() {
             nextNode = header;//nextNode 用来跟踪迭代,将之初始化为头结点
         }

         public boolean hasNext() {
             return nextNode != null;
         }

         public T next() {
             if(hasNext()){
                 Node returnNode = nextNode;
                 nextNode = nextNode.next;//其实迭代器的实现方法就是按照遍历链表的方式 来实现的
                 return returnNode.data;
             }
             else
                 throw new NoSuchElementException("Illegal call to next();" +
             "iteration is after end of list.");
         }

         //由于Iterator<T>接口中定义了remove()方法,由于 implements ,这里必须实现remove()
         //但这里并没有真正支持在迭代器操作下对单链表进行删除,而是抛异常
         public void remove() {
             throw new UnsupportedOperationException("remove() is not "+
                         "supported by this iterator");
         }
     }

     public Iterator<T> getIterator(){
         return new IteratorForLinkedList();
     }

     private Node header;//保存链表的头结点
     private Node tail;//保存链表的尾结点,采用尾插法添加结点时,不需要遍历整个链表
     private int size;//保存链表中已包含的节点数

     public LinkListWithIterator(){
         header = tail = null;//从构造器可以看出,些链表是一个不带表头结点的单链表
     }
     public LinkListWithIterator(T element){
         header = new Node(element, null);
         tail = header;
         size++;
     }

     public int length(){
         return size;
     }

     //获得索引为index处的元素值,注意:索引从0开始
     public T get(int index){
         return getNodeByIndex(index).data;
     }
     private Node getNodeByIndex(int index){
         if(index < 0 || index > size - 1)
             throw new IndexOutOfBoundsException("单链表越界");
         Node current = header;
         for(int i = 0; (i < size) && (current != null); i++, current = current.next)
             if(i == index)
                 return current;
         return null;
     }

     //返回链表中指定元素的索引,若指定元素不存在,返回-1
     public int locate(T element){
         Node current = header;
         for(int i = 0; i < size && current != null; i++, current = current.next)
             if(current.data.equals(element))
                 return i;
         return -1;
     }

     public void insert(T element, int index){
         if(index < 0 || index > size)
             throw new IndexOutOfBoundsException("单链表索引越界");
         if(header == null)//链表是空链表时
             add(element);
         else{
             if(index == 0)//在表头插入
                 addAtHeader(element);
             else{
                 Node prev = getNodeByIndex(index - 1);//获取插入结点的前驱
                 prev.next = new Node(element, prev.next);
                 size++;
             }
         }
     }

     //采用尾插法为链表增加新节点
     public void add(T element){
         if(header == null){
             header = new Node(element, null);
             tail = header;
         }
         else{
             Node newNode = new Node(element, null);
             tail.next = newNode;
             tail = newNode;
         }
         size++;
     }

     //采用头插法为链表增加新节点
     public void addAtHeader(T element){
         header = new Node(element, header);//新建的结点的next 需要指向 header结点
         if(tail == header)//如果插入之前是空链表
             tail = header;
         size++;
     }

     public T delete(int index){
         if(index < 0 || index > size - 1)
             throw new IndexOutOfBoundsException("链表索引越界");
         Node del;
         //待删除的是header节点
         if(index == 0){
             del = header;
             header = header.next;
         }
         else{
             Node prev = getNodeByIndex(index - 1);//获取待删节点的前驱
             del = prev.next;//del 指向待删除的节点
             prev.next = del.next;
         }
         del.next = null;//将待删节点从链表中脱离出去
         size--;
         return del.data;
     }

     //根据指定的元素来删除节点
     public boolean deleteByElement(T element){
         //链表为空
         if(empty()){
             return false;
         }
         //待删元素为第一个元素
         else if(header.data.equals(element)){
             Node del = header;
             header = header.next;
             if(tail == header)//说明整个链表中只有一个元素,tail也应当置null
                 tail = null;
             del.next = null;
             size --;
             return true;
         }
         //待删元素为链表中其他元素
         else{
             Node del = header.next;
             Node prev = header;
             while(del != null){
                 if(del.data.equals(element)){
                     if(tail == del)//如果待删元素是最后一个元素,需要将tail指针指向其前驱
                         tail = prev;
                     prev.next = del.next;
                     del.next = null;
                     size--;
                     return true;
                 }
                 //若没有找到element,则继续下一轮的循环
                 prev = del;
                 del = del.next;
             }
             return false;
         }
     }

     //删除链表中的最后一个元素
     public T remove(){
         return delete(size - 1);
     }

     //判断链表是否为空
     public boolean empty(){
         boolean result;
         if(size == 0){
             assert header == null;//当出现链表为空,但size不为0时,使用断言能够帮助找到逻辑错误
             result = true;
         }
         else{
             assert header != null;
             result = false;
         }
         return result;
         //return size == 0;
     }

     //清空链表
     public void clear(){
         header = tail = null;
         size = 0;
     }

     public String toString(){
         if(empty())
             return "[]";
         else{
             StringBuilder sb = new StringBuilder("[");
             for(Node current = header; current != null; current = current.next)
                 sb.append(current.data.toString() + ", ");
             int len = sb.length();
             //注意删除最后添加的两个多余的字符
             return sb.delete(len - 2, len).append("]").toString();
         }
     }
 }

最后,该程序可以作为 不带表头结点的链表 如何进行插入(头插法,尾插法、指定位置进行插入)、删除……一些基本操作的参考。

JAVA实现具有迭代器的线性表(单链表)的更多相关文章

  1. JAVA实现具有迭代器的线性表(顺序表)

    1,先了解下JAVA类库中的迭代器:JAVA提供了两种基本类型的迭代器,分别用两个接口来表示:Iterator<T>,ListIterator<T>.其中,Iterator&l ...

  2. Python线性表——单链表

    1. 线性表简介 线性表是一种线性结构,它是由零个或多个数据元素构成的有限序列.线性表的特征是在一个序列中,除了头尾元素,每个元素都有且只有一个直接前驱,有且只有一个直接后继,而序列头元素没有直接前驱 ...

  3. 数据结构之 线性表---单链表的操作B(先逆序+再删除重复元素)

    数据结构上机测试2-2:单链表操作B Time Limit: 1000MS Memory limit: 65536K 题目描述 按照数据输入的相反顺序(逆位序)建立一个单链表,并将单链表中重复的元素删 ...

  4. 数据结构之 线性表---单链表操作A (删除链表中的指定元素)

    数据结构上机测试2-1:单链表操作A Time Limit: 1000MS Memory limit: 4096K 题目描述 输入n个整数,先按照数据输入的顺序建立一个带头结点的单链表,再输入一个数据 ...

  5. java模拟表单上传文件,java通过模拟post方式提交表单实现图片上传功能实例

    java模拟表单上传文件,java通过模拟post方式提交表单实现图片上传功能实例HttpClient 测试类,提供get post方法实例 package com.zdz.httpclient; i ...

  6. C语言 严蔚敏数据结构 线性表之链表实现

    博主最近在考成都大学皇家计算机科学与技术专业,复习专业课数据结构,正好学习到线性结构中的线性表用链表这种存储结构来实现. 首先,数据结构包括1.数据的操作2.逻辑结构3.存储结构(数据结构三要素. 直 ...

  7. 【Java】 大话数据结构(2) 线性表之单链表

    本文根据<大话数据结构>一书,实现了Java版的单链表. 每个结点中只包含一个指针域的链表,称为单链表. 单链表的结构如图所示: 单链表与顺序存储结构的对比: 实现程序: package ...

  8. Java核心技术及面试指南 线性表方面的面试题总结以及答案

    3.2.7.1 请用ArrayList实现Stack以及Queue的功能. public class ArrayListStack extends ArrayList implements Stack ...

  9. 【Java】 大话数据结构(5) 线性表之双向链表

    本文根据<大话数据结构>一书,实现了Java版的双向链表. 在每个数据结点中都有两个指针,分别指向直接后继和直接前驱,这样的链表称为双向链表. 双向链表的结构如图所示: 查找元素可以根据元 ...

随机推荐

  1. 转载 linux常用的监控命令工具

    工具 简单介绍top 查看进程活动状态以及一些系统状况vmstat 查看系统状态.硬件和系统信息等iostat 查看CPU 负载,硬盘状况sar 综合工具,查看系统状况mpstat 查看多处理器状况n ...

  2. matplotlib绘图3

    #scatter fig=plt.figure() ax=fig.add_subplot(3,3,1)#3行3列 第一个图 n=128 X=np.random.normal(0,1,n) Y=np.r ...

  3. Golang的位运算操作符的使用

    & 位运算 AND | 位运算 OR ^ 位运算 XOR &^ 位清空 (AND NOT) << 左移 >> 右移 感觉位运算操作符虽然在平时用得并不多,但是在 ...

  4. Ubuntu 安装 hadoop

    安装完Linux后,我们继续(VMWare 安装 Linux  http://blog.csdn.net/hanjun0612/article/details/55095955) 这里我们开始学习安装 ...

  5. Dapper 介绍

    转载:http://***/html/itweb/20130918/125194_125199_125210.htm .NET 轻量级 ORM 框架 - Dapper 介绍 Dapper简单介绍: D ...

  6. 看完让你彻底理解 WebSocket 原理,附完整的实战代码(包含前端和后端)

    1.前言 最近有同学问我有没有做过在线咨询功能.同时,公司也刚好让我接手一个 IM 项目.所以今天抽时间记录一下最近学习的内容.本文主要剖析了 WebSocket 的原理,以及附上一个完整的聊天室实战 ...

  7. bzoj 2141 : 排队 (cdq分治+bit)

    链接: https://www.lydsy.com/JudgeOnline/problem.php?id=2141 思路: 其实就是求动态逆序对...cdq降维,用树状数组前后求两遍逆序对就好了 切水 ...

  8. (未完成...)Python3网络爬虫(2):利用urllib.urlopen向有道翻译发送数据并获得翻译结果

    环境: 火狐浏览器 pycharm2017.3.3 python3.5 1.url不仅可以是一个字符串,例如:http://www.baidu.com.url也可以是一个Request对象,这就需要我 ...

  9. 【luogu4320】道路相遇 (圆方树 + LCA)

    Description ​ 给你一张\(~n~\)个点\(~m~\)条边的无向图,保证无重边无自环, 共\(~q~\)组询问求\(~x~\)到\(~y~\)的路径上必经的点数. Solution ​ ...

  10. BZOJ2839 : 集合计数 (广义容斥定理)

    题目 一个有 \(N\) 个 元素的集合有 \(2^N\) 个不同子集(包含空集), 现在要在这 \(2^N\) 个集合中取出若干集合(至少一个), 使得它们的交集的元素个数为 \(K\) ,求取法的 ...