1,迭代器是能夠對數據結構如集合(ADT的實現)進行遍歷的對象。在遍歷過程中,可以查看、修改、添加以及刪除元素,這是它與一般的采用循環來遍歷集合中的元素不同的地方。因為,通常用循環進行的遍歷操作一般是逐個輸出元素,而用迭代器不僅僅只是查看元素,還可以改變元素(若迭代器支持remove())。
2,在JAVA類庫中定義了兩個常用的迭代器接口:Iterator和ListIterator,它們為迭代器指定了方法。給某個具體的ADT實現(如,單鏈表)添加迭代器功能時有以下兩種方式:(以鏈表為例)
①將迭代器方法添加到ADT線性表的操作中
②將迭代器定義為一個與ADT線性表相互作用的單獨的類。
②又分兩種情況:將該類定義在ADT線性表之外和將該類作為線性表的內部類(具體地說,將該類定義在實現線性表ADT接口的類的內部)
3,ADT線性表的display操作(見博文:http://m.survivalescaperooms.com/hapjin/p/4549492.html)執行了一次迭代,但是它僅僅是顯示線性表,若還想在遍歷的同時對線性表進行其它的操作呢?顯然,我們不希望每次需要遍歷ADT中的元素時,就構建循環,而是應將線性表的遍歷封裝起來。--此處可參考設計模式之迭代器模式
4,Iterator接口中只定義了三個方法
hasNext() :當迭代到最后一個元素時,再調用hasNext()將會拋出NoSuchElementException異常
next() :返回當前被迭代過的元素,剛剛被迭代器指針跳躍過的元素
remove() :該方法根據實際的需要來實現該方法,如:客戶不需要remove操作時,可以在實現remove()方法時拋出UnsupportedOperationException
注意:在JAVA中,迭代器位置不是在某個元素上,而是在集合的第一個元素之前、兩個元素之間或最后一個元素之后。
5,如何給ADT線性表添加迭代器功能呢?正如在第2點中提到的,可以有三種方式來實現
?在自己的類中實現迭代器方法,該類是公共類,與實現ADT的類分開了。這種迭代器實例為獨立類迭代器
?將遍歷操作定義為ADT操作,如:ListInterface 接口擴展了 Iterator,那么線性表對象就同時具有迭代器方法和線性表方法了。
?在自己的類中實現迭代器方法,該類是內部類,定義在實現ADT的類的內部,該類作為實現ADT類的一個私有內部類。稱之為內部類迭代器。
6,按方式?中進行迭代器的實現:
定義SeparateIterator類,implements Iterator<T>接口,在SeparateIterator類中實現對線性表的迭代。那么,如何讓迭代器去遍歷線性表呢?這就需要將迭代器與線性表聯系起來。聯系的方式如下:在SeparateIterator類中定義ListInterface類型的引用,然后構造函數中用實現了線性表接口的類的對象來實例化ListInterface引用,這樣,就可以在SeparateIterator中完成具體的對線性表的遍歷了。用戶程序只需要一個生成迭代器對象的方法,用戶程序通過調用該方法,得到一個迭代器對象,通過該迭代器對象來調用 hasNext方法、next方法 來對線性表進行遍歷。按這種方式實現的迭代器,對于線性表而言,它可以有多個迭代器同時在遍歷它。
具體代碼如下:
import java.util.Iterator;import java.util.NoSuchElementException;public class SeparateIterator<T> implements Iterator<T>{ //迭代器迭代器的對象是誰?它就是實現ListInterface接口的類的對象 PRivate ListInterface<T> list;//用來將迭代器與線性表聯系起來 private int nextPosition;//指示當前正在遍歷的元素 //執行remove時需要先調用next private boolean wasNextCalled;//用來標記next是否被調用了 /* * 構造器方法,用來初始化迭代器 * 參數類型為ListInterface,這樣,任何實現了該接口的類的對象 * 都可以作為參數傳遞給構造器,這是JAVA中的多態的性質 */ public SeparateIterator(ListInterface<T> aList) { list = aList; nextPosition = 0; wasNextCalled = false; } @Override public boolean hasNext() { return nextPosition < list.getLength(); } @Override public T next() { if(hasNext()){ wasNextCalled = true; nextPosition++; return list.getEntry(nextPosition);//調用線性表的方法getEntry來進行實際的遍歷 } else throw new NoSuchElementException("Illegal call to next(); " + "iterator is after end of list"); } @Override public void remove() { if(wasNextCalled){ list.remove(nextPosition); nextPosition--; wasNextCalled = false; } else throw new IllegalStateException("Illegal call to remove(); " + "next() was not called."); }}方式?和方式?中對迭代器的實現基本上相同。方式?就是:ListInterface 接口擴展了 Iterator,這樣具體實現ListInterface接口的類除了需要實現基本的線性表的方法(如,getEntry、add、clear、replace……)還需要實現Iterator中定義的三個方法。
這樣當 new 一個實現了ListInterface接口的類的對象時,該對象就具有兩種性質:第一種性質是線性表,第二種性質是迭代器。說白了,即實現了ListInterface接口的類的對象它既是線性表又是迭代器。因為它即可以調用線性表的各種方法,又可以調用hasNext方法、next方法進行迭代。
對方式?而言,它是在實現ListInterface接口的類的內部定義一個實現Iterator接口的類(如,實現ListInterface接口的類為LList,則是在LList類的內部定義一個類,這個類 implements Iterator<T>)
這樣做的好處是什么呢?內部類可以直接訪問其外部類中的數據,因此,以內部類的方式實現迭代器就天然地將需要迭代的對象(線性表)與迭代器聯系起來了。相比于方式?而言,方式?更高效,因為方式?是直接遍歷線性表,而方式?是通過調用線性表的方法來遍歷線性表。
方式?的具體實現代碼如下:由于迭代器由內部類實現,因此這里貼出了整個類(包括外部類)的代碼:
1 package linklist; 2 3 import java.util.Iterator; 4 import java.util.NoSuchElementException; 5 6 public class LList<T> implements ListInterface<T>{ 7 8 private Node firstNode;//指向第一個結點的指針,該鏈表是不帶頭結點的單鏈表 9 private int length;//表示單鏈表的長度 10 11 //Node類中不需要定義訪問屬性的get方法以及set方法,因為Node是內部類,內部類的屬性可以直接在外部類中被訪問 12 class Node{ 13 //Node是內部類,其外部類中已經定義了T,故可以在這里使用通配符T 14 private T data;//結點的數據部分 15 private Node next;//結點的指針部分,指向下一個結點 16 //Node類中不需要默認構造器 17 public Node(T dataPortion){ 18 data = dataPortion; 19 } 20 public Node(T dataPortion, Node nextNode){ 21 data = dataPortion; 22 next = nextNode; 23 } 24 } 25 26 /* 27 * 需要在外部類(LList.java)中添加getIterator方法,該方法用來獲得迭代器對象 28 * 這樣,外部類對象調用getIterator()得到迭代器對象,該迭代器對象調用next進行線性表的迭代 29 * 30 */ 31 public Iterator<T> getIterator(){ 32 return new IteratorForLinkList(); 33 } 34 35 private class IteratorForLinkList implements Iterator<T>{ 36 private Node nextNode;//標記當前正在遍歷的元素 37 private IteratorForLinkList(){ 38 nextNode = firstNode;//在內部類中直接訪問外部類中的firstNode數據域 39 } 40 @Override 41 public boolean hasNext() { 42 return nextNode != null; 43 } 44 @Override 45 public T next() { 46 if(hasNext()){ 47 Node returnNode = nextNode; 48 nextNode = nextNode.next; 49 return returnNode.data; 50 } 51 else//已經遍歷到線性表末尾或線性表為空 52 throw new NoSuchElementException("Illegal call to next()" + "iterator is after end of list."); 53 } 54 @Override 55 public void remove() {//該迭代器不支持remove()方法 56 throw new UnsupportedOperationException("remove() is not supported by this iterator"); 57 } 58 } 59 60 public LList(){ 61 clear(); 62 } 63 //獲取鏈表中指定位置處的結點 64 private Node getNodeAt(int givenPosition){ 65 assert (!isEmpty() && ((1 <= givenPosition) && (givenPosition <= length))); 66 Node currentNode = firstNode; 67 for(int counter = 1; counter < givenPosition; counter++){ 68 currentNode = currentNode.next; 69 } 70 assert currentNode != null; 71 return currentNode; 72 } 73 74 @Override 75 public boolean add(T newEntry) { 76 // 將每個新結點插入到鏈表的末尾,通過getNodeAt()方法來獲得最后一個元素的地址 77 Node newNode = new Node(newEntry); 78 if(isEmpty()){//插入第一個結點 79 firstNode = newNode; 80 } 81 else{//在其它位置插入結點 82 Node lastNode = getNodeAt(length);//這里每插入一個元素都需要遍歷一次鏈表,代價較大 83 lastNode.next = newNode; 84 } 85 length++; 86 return true; 87 } 88 89 @Override 90 public boolean add(int givenPosition, T newEntry){//在指定位置處插入結點 91 boolean isSuccessful = true; 92 if(givenPosition >= 1 && givenPosition <= length + 1){ 93 Node newNode = new Node(newEntry); 94 if(isEmpty() || givenPosition == 1){//在第一個位置處插入結點 95 newNode.next = firstNode; 96 firstNode = newNode; 97 } 98 else{//在其它位置插入結點 99 Node nodeBefore = getNodeAt(givenPosition - 1);100 Node nodeAfter = nodeBefore.next;101 nodeBefore.next = newNode;102 newNode.next = nodeAfter;103 }104 length++;105 }106 else107 isSuccessful = false;108 return isSuccessful;109 }110 111 @Override112 public final void clear() {//clear()在構造器中被調用了,所以此外用final修飾符113 firstNode = null;114 length = 0;115 }116 117 @Override118 public T remove(int givenPosition) {//刪除指定位置處的結點119 T result = null;120 if((!isEmpty()) && ((givenPosition >= 1) && (givenPosition <= length))){121 if(givenPosition == 1){//刪除第一個位置處的結點122 result = firstNode.data;123 firstNode = firstNode.next;124 }125 else//刪除表中其它位置結點126 {127 Node nodeBefore = getNodeAt(givenPosition - 1);128 Node nodeToRemove = nodeBefore.next;129 Node nodeAfter = nodeToRemove.next;130 nodeBefore.next = nodeAfter;131 result = nodeToRemove.data;132 }133 length--;134 }135 return result;136 }137 138 @Override139 public boolean replace(int givenPosition, T newEntry) {//替換指定位置處結點的值140 boolean isSuccessful = true;141 if((!isEmpty()) && ((givenPosition >= 1) && (givenPosition <= length))){142 Node desireNode = getNodeAt(givenPosition);143 desireNode.data = newEntry;144 }145 else146 isSuccessful = false;147 return isSuccessful;148 }149 150 @Override151 public T getEntry(int givenPosition) {//獲取指定位置的結點的值152 T result = null;153 if((!isEmpty()) && ((givenPosition >= 1) && (givenPosition <= length))){154 result = getNodeAt(givenPosition).data;155 }156 return result;157 }158 159 @Override160 public boolean contains(T anEntry) {//判斷鏈表中的結點是否包含某個值161 boolean found = false;162 Node currentNode = firstNode;163 while(!found && currentNode != null){164 if(currentNode.data.equals(anEntry)){165 found = true;166 break;167 }168 currentNode = currentNode.next;169 }170 return found;171 }172 173 @Override174 public int getLength() {//獲取鏈表的長度175 return length;176 }177 178 @Override179 public boolean isEmpty() {//判斷鏈表是否為空180 boolean result;181 if(length == 0){182 assert firstNode == null;183 result = true;184 }185 else{186 assert firstNode != null;187 result = false;188 }189 return result;190 }191 192 @Override193 public void display() {//遍歷鏈表,顯示鏈表中的每個結點的值194 Node currentNode = firstNode;195 while(currentNode != null){196 System.out.println(currentNode.data);197 currentNode = currentNode.next;198 }199 }200 }可以將該LList.java 與 http://m.survivalescaperooms.com/hapjin/p/4549492.html 博客中的LList.java 比較。(注意新增的第26--58行代碼,內部類迭代器的具體實現代碼)
前者是帶有迭代器的線性表實現,后者是不帶迭代器的線性表實現。
新聞熱點
疑難解答