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

一,迭代器的基本知识:

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

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

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

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

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

 1 public Interface ListInterface<T>{
 2     //此处定义了链表的基本操作
 3 }
 4 
 5 public class LinkList<T> implements ListInterface<T>{
 6     //LinkList类实现了ADT
 7     //此处实现链表的基本操作
 8 }
 9 public class SeparateIterator<T> implements Iterator<T>
10 {
11     //此处实现了Iterator接口中的方法
12 }
13 
14 //将实现了ADT类(即,LinkList)的对象作为构造函数的参数传入到实现迭代器类的构造方法中
15 //从而实现它们之间的交互
16 Iterator<T> it = new SeparateIterator<T>(LinkList实例);

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

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

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

 

二,具体的实例的代码

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

 1 public interface ListInterface<T> {
 2     //返回当前链表中元素个数
 3     public int length();
 4     
 5     ////获得索引为index处的元素值,注意:索引从0开始
 6     public T get(int index);
 7     
 8     //返回链表中指定元素的索引,若指定元素不存在,返回-1
 9     public int locate(T element);
10     
11     //在指定索引index处添加指定的element
12     public void insert(T element, int index);
13     
14     //采用尾插法为链表增加新节点
15     public void add(T element);
16     
17     //采用头插法为链表增加新节点
18     public void addAtHeader(T element);
19     
20     //删除指定索引处的节点
21     public T delete(int index);
22     
23     //删除线性表中的最后一个元素
24     public T remove();
25     
26     //清空线性表
27     public void clear();
28 }

 

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>就表示一个带有迭代器的单链表了。

1 import java.util.Iterator;
2 
3 public interface ListWithIteratorInterface<T> extends ListInterface<T>{
4     public Iterator<T> getIterator();
5 }

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

  1 import java.util.Iterator;
  2 import java.util.NoSuchElementException;
  3 
  4 public class LinkListWithIterator<T> implements ListWithIteratorInterface<T>{
  5     private class Node{
  6         private T data;
  7         private Node next;
  8         private Node(){
  9             
 10         }
 11         private Node(T data, Node next){
 12             this.data = data;
 13             this.next = next;
 14         }
 15     }
 16     /*
 17      * 该内部类用来实现迭代器,从而能够对单链表进行迭代
 18      * 使用内部类好处:能够直接访问ADT(抽象数据类型,如此例中的链表)的数据域,迭代效率高
 19      */
 20     private class IteratorForLinkedList implements Iterator<T>{
 21 
 22         private Node nextNode;//Node类型是通过私有内部类定义的表示链表结点的一种数据类型
 23         private IteratorForLinkedList() {
 24             nextNode = header;//nextNode 用来跟踪迭代,将之初始化为头结点
 25         }
 26         
 27         public boolean hasNext() {
 28             return nextNode != null;
 29         }
 30 
 31         public T next() {
 32             if(hasNext()){
 33                 Node returnNode = nextNode;
 34                 nextNode = nextNode.next;//其实迭代器的实现方法就是按照遍历链表的方式 来实现的
 35                 return returnNode.data;
 36             }
 37             else
 38                 throw new NoSuchElementException("Illegal call to next();" + 
 39             "iteration is after end of list.");
 40         }
 41 
 42         //由于Iterator<T>接口中定义了remove()方法,由于 implements ,这里必须实现remove()
 43         //但这里并没有真正支持在迭代器操作下对单链表进行删除,而是抛异常
 44         public void remove() {
 45             throw new UnsupportedOperationException("remove() is not "+
 46                         "supported by this iterator");
 47         }        
 48     }
 49     
 50     public Iterator<T> getIterator(){
 51         return new IteratorForLinkedList();
 52     }
 53     
 54     private Node header;//保存链表的头结点
 55     private Node tail;//保存链表的尾结点,采用尾插法添加结点时,不需要遍历整个链表
 56     private int size;//保存链表中已包含的节点数
 57     
 58     public LinkListWithIterator(){
 59         header = tail = null;//从构造器可以看出,些链表是一个不带表头结点的单链表
 60     }
 61     public LinkListWithIterator(T element){
 62         header = new Node(element, null);
 63         tail = header;
 64         size++;
 65     }
 66     
 67     public int length(){
 68         return size;
 69     }
 70     
 71     //获得索引为index处的元素值,注意:索引从0开始
 72     public T get(int index){
 73         return getNodeByIndex(index).data;
 74     }
 75     private Node getNodeByIndex(int index){
 76         if(index < 0 || index > size - 1)
 77             throw new IndexOutOfBoundsException("单链表越界");
 78         Node current = header;
 79         for(int i = 0; (i < size) && (current != null); i++, current = current.next)
 80             if(i == index)
 81                 return current;
 82         return null;
 83     }
 84     
 85     //返回链表中指定元素的索引,若指定元素不存在,返回-1
 86     public int locate(T element){
 87         Node current = header;
 88         for(int i = 0; i < size && current != null; i++, current = current.next)
 89             if(current.data.equals(element))
 90                 return i;
 91         return -1;
 92     }
 93 
 94     public void insert(T element, int index){
 95         if(index < 0 || index > size)
 96             throw new IndexOutOfBoundsException("单链表索引越界");
 97         if(header == null)//链表是空链表时
 98             add(element);
 99         else{
100             if(index == 0)//在表头插入
101                 addAtHeader(element);
102             else{
103                 Node prev = getNodeByIndex(index - 1);//获取插入结点的前驱
104                 prev.next = new Node(element, prev.next);
105                 size++;
106             }
107         }
108     }
109     
110     //采用尾插法为链表增加新节点
111     public void add(T element){
112         if(header == null){
113             header = new Node(element, null);
114             tail = header;
115         }
116         else{
117             Node newNode = new Node(element, null);
118             tail.next = newNode;
119             tail = newNode;
120         }
121         size++;
122     }
123     
124     //采用头插法为链表增加新节点
125     public void addAtHeader(T element){
126         header = new Node(element, header);//新建的结点的next 需要指向 header结点
127         if(tail == header)//如果插入之前是空链表
128             tail = header;
129         size++;
130     }
131     
132     public T delete(int index){
133         if(index < 0 || index > size - 1)
134             throw new IndexOutOfBoundsException("链表索引越界");
135         Node del;
136         //待删除的是header节点
137         if(index == 0){
138             del = header;
139             header = header.next;
140         }
141         else{
142             Node prev = getNodeByIndex(index - 1);//获取待删节点的前驱
143             del = prev.next;//del 指向待删除的节点
144             prev.next = del.next;
145         }
146         del.next = null;//将待删节点从链表中脱离出去
147         size--;
148         return del.data;
149     }
150     
151     //根据指定的元素来删除节点
152     public boolean deleteByElement(T element){
153         //链表为空
154         if(empty()){
155             return false;
156         }
157         //待删元素为第一个元素
158         else if(header.data.equals(element)){
159             Node del = header;
160             header = header.next;
161             if(tail == header)//说明整个链表中只有一个元素,tail也应当置null
162                 tail = null;
163             del.next = null;
164             size --;
165             return true;
166         }
167         //待删元素为链表中其他元素
168         else{
169             Node del = header.next;
170             Node prev = header;
171             while(del != null){
172                 if(del.data.equals(element)){
173                     if(tail == del)//如果待删元素是最后一个元素,需要将tail指针指向其前驱
174                         tail = prev;
175                     prev.next = del.next;
176                     del.next = null;
177                     size--;
178                     return true;
179                 }
180                 //若没有找到element,则继续下一轮的循环
181                 prev = del;
182                 del = del.next;
183             }
184             return false;
185         }
186     }
187     
188     //删除链表中的最后一个元素
189     public T remove(){
190         return delete(size - 1);
191     }
192     
193     //判断链表是否为空
194     public boolean empty(){
195         boolean result;
196         if(size == 0){
197             assert header == null;//当出现链表为空,但size不为0时,使用断言能够帮助找到逻辑错误
198             result = true;
199         }
200         else{
201             assert header != null;
202             result = false;
203         }
204         return result;
205         //return size == 0;
206     }
207     
208     //清空链表
209     public void clear(){
210         header = tail = null;
211         size = 0;
212     }
213     
214     public String toString(){
215         if(empty())
216             return "[]";
217         else{
218             StringBuilder sb = new StringBuilder("[");
219             for(Node current = header; current != null; current = current.next)
220                 sb.append(current.data.toString() + ", ");
221             int len = sb.length();
222             //注意删除最后添加的两个多余的字符
223             return sb.delete(len - 2, len).append("]").toString();
224         }
225     }
226 }

 

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

    原文作者:hapjin
    原文地址: https://www.cnblogs.com/hapjin/p/4452018.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞