1,先了解下JAVA类库中的迭代器:JAVA提供了两种基本类型的迭代器,分别用两个接口来表示:Iterator<T>,ListIterator<T>。其中,Iterator<T>接口中只定义了三个方法:hasNext()、iterator()、next(),而ListIterator<T>中,除了拥有前面所述的三种方法外,而另外拥有hasPrevious()、previous()、remove()、set()等其他方法(具体参考JDK文档)。

这说明:实现了Iterator<T>接口的迭代器只能往一个方向遍历(正向),而实现了ListIterator<T>接口的迭代器即可以正向遍历又可以反向遍历(由hasPrevious()、previous()实现反向遍历),同时,它还可以对遍历的元素进行修改(由set()方法实现)、删除当前遍历的元素(由remove()实现)。

2,当前,在写程序时,最好能够使用JAVA类库中已经为我们定义好的迭代器,它为JAVA的集合类都定义了返回上述两种迭代器的方法。

如:ArrayList<T>类的 iterator() 方法返回一个Iterator<T>类型的只能对单一方向进行迭代的迭代器,而listIterator()方法返回一个ListIterator<T>类型的迭代器,它不仅能双向迭代,而且修改、删除迭代的元素。

3,下面实例代码实现了一种ADT之顺序表(实质为数组),并且该数组拥有遍历器。该遍历器能对当前遍历的数组进行修改和删除。个人感觉要想同时保证迭代器具有修改和删除的功能,同时又要保证ADT基本操作的正确,对数组下标的合适定义真的很难。

4,首先定义了一个接口ListWithListIteratorInterface,该接口只有一个方法getIterator(),该方法负责生成一个迭代器并返回给调用者。然后,再让实现了数组的类SequenceListWithIterator<T> implements ListWithListIteratorInterface<T>.

为什么要这样做呢?为什么不用SequenceListWithIterator直接 implements ListIterator<T> ?

因为,如果用SequenceListWithIterator直接 implements ListIterator<T>,那么SequenceListWithIterator类的对象不仅是一个迭代器(因为它implements ListIterator<T>)而且是一个顺序表(因为它实现了线性表中的各种基本操作,相当于implments ListInterface<T>了)。这意味着,在我们的代码中,当创建了一个SequenceListWithIterator对象时,该对象不能在同一时刻对线性表进行多次迭代,因为它本身就是迭代器,只能对"自身"元素进行迭代了。

而采用SequenceListWithIterator<T> implements ListWithListIteratorInterface<T>,这样SequenceListWithIterator对象只是线性表,它不是迭代器。但是,由于它实现了ListWithListIteratorInterface<T>,它(顺序表对象)就可以调用getIterator()方法获得一个迭代对象,让该迭代器对象对自己进行遍历,当然了,它就可以多次调用getIterator()方法获得多个迭代器对象,从而可以让多个迭代器同时对自己进行遍历。

接口ListWithListIteratorInterface具体代码如下:

import java.util.ListIterator;

public interface ListWithListIteratorInterface<T> {
    public ListIterator<T> getIterator();
}

实现了线性表的基本操作(说明它是一个顺序表)及拥有一个实现了ListIterator<T>接口内部类(说明它拥有一个迭代器)的具体代码如下:

import java.util.Arrays;
import java.util.ListIterator;
import java.util.NoSuchElementException;

public class SequenceListWithIterator<T> implements
        ListWithListIteratorInterface<T> {
    private final int DEFAULT_SIZE = 16;// final实例变量显示指定初始值,且不再变化。

    private Object[] elementData;// 该数组用来保存顺序表中的元素
    private int capacity;// 保存数组的长度
    private int size;// 保存顺序表中当前元素的个数

    private enum Move {
        NEXT, PREVIOUS
    };

    private class IteratorForSequenceList implements ListIterator<T> {
        private int nextIndex;// 标记迭代器指针的位置
        private boolean isRemoveOrSetLegal;// 标记 remove() 或 set() 操作是否合法
        private Move lastMove;// 当进行了一次移动操作后,lastMove指示这是前移而是后移

        private IteratorForSequenceList() {
            nextIndex = 0;
            isRemoveOrSetLegal = false;//初始值为false,当调用了next()或previous()时被设置为true
            lastMove = null;
        }

        public boolean hasNext() {
            return nextIndex < size - 1;
        }

        public T next() {
            if (hasNext()) {
                lastMove = Move.NEXT;// 进行的是后移操作
                isRemoveOrSetLegal = true;// 当next()调用了之后,才可以调用remove()或set()
                return (T) elementData[nextIndex++];// 注意nextIndex的自增顺序
            } else
                throw new NoSuchElementException("Illegal call to next(),"
                        + "迭代器位于表的最后端");
        }

        public boolean hasPrevious() {
            return (nextIndex > 0) && (nextIndex <= size);
        }

        public T previous() {
            if (hasPrevious()) {
                lastMove = Move.PREVIOUS;// 进行的是前移操作
                isRemoveOrSetLegal = true;
                return (T) elementData[--nextIndex];// 注意nextIndex的自减顺序
            } else
                throw new NoSuchElementException("Illegal call to previous()"
                        + "iterator is before beginning of list");
        }

        // 返回当前元素的索引
        public int nextIndex() {
            int result;
            if (hasNext())
                result = nextIndex + 1;
            else
                result = size;// 迭代超出顺序表的最后一个元素时返回顺序表中元素的个数
            return result;
        }

        // 返回当前元素的前一个元素的索引
        public int previousIndex() {
            int result;
            if (hasPrevious())
                result = nextIndex - 1;
            else
                result = -1;// 迭代在顺序表的第一个元素时,返回-1
            return result;
        }

        // 删除当前迭代的元素
        public void remove() {
            if (isRemoveOrSetLegal) {
                isRemoveOrSetLegal = false;
                if (lastMove.equals(Move.NEXT)) {
                    // next()被调用,但add() remove()没有被调用
                    SequenceListWithIterator.this.delete(nextIndex - 1);
                    nextIndex--;
                } else if (lastMove.equals(Move.PREVIOUS)) {
                    SequenceListWithIterator.this.delete(nextIndex);
                }
            } else
                throw new IllegalStateException(
                        "Illegal call to remove();"
                                + "next() or previous() was not called, OR add() or remove() called since then");
        }

        // 更改当前迭代的元素
        public void set(T e) {
            if (isRemoveOrSetLegal) {
                if (lastMove.equals(Move.NEXT))
                    // 使用内部类机制,内部类里面可以可以直接访问它外部类的底层数据
                    elementData[nextIndex - 1] = e;
                else {
                    assert lastMove.equals(Move.PREVIOUS);
                    elementData[nextIndex] = e;
                }
            } else {
                throw new IllegalStateException(
                        "Illegal call to set();"
                                + "next() or previous() was not called, OR add() or remove() called since then");
            }

        }

        // 在迭代器的当前元素之前添加一个元素,注意是之前
        public void add(T e) {
            // set 和 remove 操作只能是在迭代器访问过(跳跃过)某元素之后才能进行
            isRemoveOrSetLegal = false; // 进行add操作之后,不能立即进行set() 或者 remove()
            SequenceListWithIterator.this.insert(e, nextIndex++);
        }

    }

    // 以默认的大小创建顺序表
    public SequenceListWithIterator() {
        capacity = DEFAULT_SIZE;
        elementData = new Object[capacity];
    }

    // 以指定的大小创建顺序表
    public SequenceListWithIterator(int initSize) {
        capacity = 1;
        while (capacity < initSize)
            capacity <<= 1;// 将capacity设置成大于initSize的最小2次方
        elementData = new Object[capacity];
    }

    public ListIterator<T> getIterator() {
        return new IteratorForSequenceList();
    }

    // 获取顺序表中当前元素的个数
    public int length() {
        return size;
    }

    // 获取顺序表中索引为 i 处的元素,i表示索引,即以 0 开始
    public T get(int i) {
        if (i < 0 || i > size - 1)
            throw new IndexOutOfBoundsException("顺序表索引越界");
        return (T) elementData[i];
    }

    // 查看顺序表中指定元素的索引,若未找到,返回-1
    public int locate(T element) {
        for (int i = 0; i < size; i++)
            if (elementData[i].equals(element))
                return i;
        return -1;
    }

    // 在顺序表的指定索引处插入一个元素
    public void insert(T element, int index) {
        if (index < 0 || index > size)
            throw new IndexOutOfBoundsException("顺序表索引越界");
        ensureCapacity(size + 1);// 确保顺序表满时进行扩容,从而能插入元素
        // 将指定索引后的所有元素向后移动一个位置
        // System.arraycopy(elementData, index, elementData, index + 1, size -
        // index);
        for (int i = size; i > index; i--)
            elementData[i] = elementData[i - 1];
        elementData[index] = element;
        size++;// 顺序表中的元素个数增1
    }

    private void ensureCapacity(int minCapacity) {
        // 当数组容量已满时,对数组进行扩容。将容量扩展到大于minCapacity的最小2的次方
        if (minCapacity > capacity) {
            while (capacity < minCapacity)
                capacity <<= 1;
            elementData = Arrays.copyOf(elementData, capacity);
        }
    }

    // 在顺序表的末尾添加一个元素
    public void add(T element) {
        insert(element, size);
    }

    // 删除顺序表中指定索引处的元素
    public T delete(int index) {
        if (index < 0 || index > size - 1)
            throw new IndexOutOfBoundsException("顺序表索引越界");
        T oldValue = (T) elementData[index];
        int numMoved = size - index - 1;// 计算需要移动的元素个数
        if (numMoved > 0) {
            System.arraycopy(elementData, index + 1, elementData, index,
                    numMoved);
        }
        elementData[--size] = null;// 让垃圾回收器及时回收,避免内存泄露
        return oldValue;
    }

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

    // 判断顺序表是否为空表
    public boolean empty() {
        return size == 0;
    }

    // 清空顺序表
    public void clear() {
        Arrays.fill(elementData, null);// 将数组elementData中的每个元素都赋值null
        size = 0;
    }

    public String toString() {
        if (size == 0)
            return "[]";
        else {
            StringBuilder sb = new StringBuilder("[");
            for (int i = 0; i < size; i++)
                sb.append(elementData[i].toString() + ", ");
            int len = sb.length();
            // 删除由于上面for循环中最后添加的多余的两个字符 (一个是逗号,一个是空格符号)
            return sb.delete(len - 2, len).append("]").toString();
        }
    }

}

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

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

    一,迭代器的基本知识: 1,为什么要用迭代器?(迭代:即对每一个元素进行一次“问候”) 比如说,我们定义了一个ADT(抽象数据类型),作为ADT的一种实现,如单链表.而单链表的基本操作中,大部分需要用 ...

  2. C语言 线性表 顺序表结构 实现

    一个能够自动扩容的顺序表 ArrList (GCC编译). #include <stdio.h> #include <stdlib.h> #include <string ...

  3. 【数据结构】线性表&&顺序表详解和代码实例

    喜欢的话可以扫码关注我们的公众号哦,更多精彩尽在微信公众号[程序猿声] 01 预备知识 1.0 什么是线性表? 线性表(List)是零个或者多个数据元素的有限序列. 首先它是一个序列.里面的元素是有顺 ...

  4. 线性表——顺序表的实现与讲解(C++描述)

    线性表 引言 新生安排体检,为了 便管理与统一数据,学校特地规定了排队的方式,即按照学号排队,谁在前谁在后,这都是规定好的,所以谁在谁不在,都是非常方便统计的,同学们就像被一条线(学号)联系起来了,这 ...

  5. 数据结构学习java(一点五)链式顺序表(链表)

    java中没有将指针暴露给用户(以前做过看过一篇文章写有java中是有指针的,只是被藏起来了),所以得使用引用的方式. 何为引用请看下面这篇文章(写的很不错,当然肯定比我写的好): https://w ...

  6. 线性表中顺序表的的理解和实现(java)

    线性表的顺序表示指的是用一组地址连续的存储单元以此存储线性表的数据元素,这种表示也称作线性表的顺序存储结构或顺序映像.通常,称这种存储结构的线性表为顺序表.特点是:逻辑上相邻的数据元素,其物理次序上也 ...

  7. 数据结构Java实现02----线性表与顺序表

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...

  8. Java实现顺序表

    利用顺序存储结构表示的顺序表称为顺序表. 它用一组连续的地址存储单元一次存放线性表中的数据元素. 顺序表的实现是数据结构中最简单的一种. 由于代码中已经有详细注释,代码外不再阐述. 下次再陈上关于顺序 ...

  9. 数据结构----线性表顺序和链式结构的使用(c)

    PS:在学习数据结构之前,我相信很多博友也都学习过一些语言,比如说java,c语言,c++,web等,我们之前用的一些方法大都是封装好的,就java而言,里面使用了大量的封装好的方法,一些算法也大都写 ...

随机推荐

  1. Oracle 控制文件管理

    控制文件是一个很小的二进制文件(10MB左右),含有数据库结构信息,包括数据文件和日志文件信息.控制文件在数据库创建时被自动创建,并在数据库发生物理变数时更新.控制文件被不断更新,在任何时候都要保证控 ...

  2. Node 开启

    cmd    //进入命令行 D:     //指定磁盘 cd   文件路径   //指定路径 node 文件名.js       //执行文件 增补: Node执行js文件自动嵌套 (functio ...

  3. centos7 搭建svn服务器

    1.安装svn服务器: yum install subversion 2.配置svn服务器: 建立svn版本库根目录及相关目录即svndata及密码权限命令svnpasswd: mkdir -p /a ...

  4. 设计模式之工厂模式(c++)

    问题描述 在面向对象系统设计中经常可以遇到以下的两类问题:1)为了提高内聚(Cohesion)和松耦合(Coupling),我们经常会抽象出一些类的公共接口以形成抽象基类或者接口.这样我们可以通过声明 ...

  5. 在保存Bitmap的时候出现“GDI出现一般性错误”

    今天开发的时候出现过一个非常奇怪的问题,在保存最终的Bitmap图片的时候,明明使用Directory.Exist(filePath)函数判断当前路径的时候,这些路径都是有用的并且都是合法的,但是就是 ...

  6. Python——socketsever模块

    1.作用:同时与多个客户端通信 import socketserver class MyServer(skcketserver.BaseRequestHandler): def handle(self ...

  7. Lodop客户端本地角色注册号常见误区

    之前写过一篇关于Lodop和c-lodop注册号的区别:LODOP.C-LODOP注册号的区别第一种角色客户端本地打印角色是最常见的角色,最常见的场景,关于c-lodop云打印,它的第一种角色是取代L ...

  8. target存放的是编译后的.class文件地方 默认情况下不会讲非class文件放入进入 如果要使用非.class文件 需要通过增加配置方式自动加入文件

    target存放的是编译后的.class文件地方 默认情况下不会讲非class文件放入进入 如果要使用非.class文件 需要通过增加配置方式自动加入文件

  9. mysql Packet for query is too large (2036 > 1024). You can change this value on the server by setting the max_allowed_packet' variable.

    解决方法: 打开控制台,输入下面语句,执行 set global max_allowed_packet = 20*1024*1024; 网上说要重启 mysql server, 我是执行完后不用重启就 ...

  10. 【转】位置式、增量式PID算法C语言实现

    位置式.增量式PID算法C语言实现 芯片:STM32F107VC 编译器:KEIL4 作者:SY 日期:2017-9-21 15:29:19 概述 PID 算法是一种工控领域常见的控制算法,用于闭环反 ...