【設計模式】Iterator迭代器設計模式(容器和容器的遍歷)
阿新 • • 發佈:2019-01-09
在遍歷容器元素的時候,有很多初學者在疑惑,為什麼返回一個iterator我就能夠去遍歷這個容器了呢?
今天我們就來深入剖析一下迭代器iterator的設計模式(循序漸進的剖析,一定要耐心看完)
iterator是"四人幫"所定義的23種設計模式之一(不太難,也不是非常重要,只是在遍歷容器的時候能夠用到)
首先需要讀這個總結的同志掌握面向物件的思想。
1.我們先自己寫一個可以動態新增物件的容器
建立一個新的Java工程:Iterator
建立一個包:cn.edu.hpu.iterator
在包下建立類ArrayList:
我們要在容器內首先新增一個add方法,用於向容器內新增元素
再寫一個size方法,用於查詢容器元素個數
ArrayList.java:
我們就完成了這樣一個容器,這個容器是用陣列來實現的。容器比陣列好在哪裡呢?使用容器就不需要考慮到陣列的邊界問題了,容器可以動態拓展。想往裡面裝就add就行了,想知道大小直接size就可以了。
2.我們加一個輔助類Cat
Cat.java:
以後新增元素就新增這個就可以了,因為可以通過id值知道添加了哪些資料
如測試:
3.OK,現在我膩歪了,不想用ArrayList容器了,我想用其它容器去裝Cat,這個新的容器我們底層不用陣列實現了,我們用連結串列來實現。(需要你先了解資料結構中的連結串列概念)
建立一個新容器類LinkedList,一個節點類Node
LinkedList.java:
Node.java:
測試:
我們實現了兩個內部原理完全不同的容器,那麼下面我們來看:
4.考慮容器的可替換性
什麼叫做可替換性?考慮我們使用容器的客戶端類,想使這些容器可以任意的替換,其它程式碼不用改變,這就需要我們把所有容器的對外提供的方法統一起來。
解決方案:
我們寫一個統一的介面Collection,定義一些統一的規範,讓所有容器去實現這個介面。
Collection.java:
ArrayList.java:
LinkedList.java:
這樣我們就用Collection介面來實現對不同容器對外方法的統一。
在我們測試的時候就可以使用這種方式來實現容器:
Collection c=new ArrayList();
我對ArrayList不滿意,就可以改成
Collection c=new LinkedList();
小結:針對介面程式設計就不需要考慮具體的實現類是什麼,所以你的程式會更加靈活,要替換其它實現的時候,只需換一個位置(當然如果你寫在配置檔案上,你連程式碼都不用改)。
5.接下來重點來了,我們的容器需要一個非常非常重要的功能-------遍歷
我想遍歷容器中的所有元素,如何去做?
當然可以這樣來寫:
20
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
但是,當我們將Collection的實現改為LinkedList的時候下面的遍歷方法就不行了。
這個時候就需要一種統一的遍歷方式。
每一種容器都應該有它自己的一種遍歷方式,我們要想方設法的給他們統一起來,但是每種容器的遍歷器的實現又不一樣,所以統一遍歷方式只能用一種共同的實現方式,介面或抽象類的方式。
所以我們這麼寫:
定義一個介面Iterator:
其中有兩個未實現的方法:next和hasNext。
我要求任何一個容器都必須給我一個實現了這個介面的類的物件,
具體的實現交給具體的容器自己去實現。實現完了之後,我肯定知道,
這個物件就實現這兩個方法了,有了這兩個方法,我就可以實現對這
個容器的遍歷了。
我們在Collection中加入返回Iterator的未實現方法
ArrayList實現了iterator()這個方法
測試:
6.當然我沒還沒有寫Iterator的實現,我們來寫實現:
我們在ArrayList中寫一個內部類ArrayListIterator,實現Iterator介面:
測試:
20
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
在我們的JavaAPI中的ArrayList也實現了Java的Collection介面,同樣提供了一個方法,
叫iterator(),這個方法返回了一個實現了Iterator介面的物件。
現在大家明白iterator()方法到底是幹什麼用的,它返回的介面又是什麼意思了吧?
作業:
這個LinkedList也需要寫一個iterator方法,返回一個實現了Iterator的物件。該如何寫?
這是我自己寫的:
感謝馬士兵老師的"設計模式"視訊提供的幫助
今天我們就來深入剖析一下迭代器iterator的設計模式(循序漸進的剖析,一定要耐心看完)
iterator是"四人幫"所定義的23種設計模式之一(不太難,也不是非常重要,只是在遍歷容器的時候能夠用到)
首先需要讀這個總結的同志掌握面向物件的思想。
1.我們先自己寫一個可以動態新增物件的容器
建立一個新的Java工程:Iterator
建立一個包:cn.edu.hpu.iterator
在包下建立類ArrayList:
我們要在容器內首先新增一個add方法,用於向容器內新增元素
再寫一個size方法,用於查詢容器元素個數
ArrayList.java:
package cn.edu.hpu.iterator; public class ArrayList { //我們要用陣列模擬一個可以新增任意大小元素的容器 //首先我們固定一個數組空間,再新增元素的時候,我們去判斷 //這個空間是不是滿了,如果滿了再在此基礎上再新增一個固定空間 //往下依次類推,這樣就有了可以新增任意大小元素的容器 //當然,容器大小一定不可能超過計算機記憶體的大小 Object[] objects=new Object[10]; int index=0;//代表objects的下一個空的位置在哪裡 //新增元素方法 public void add(Object o){ if(index==objects.length){ //原來的陣列已滿,要做擴充套件(原長度X2) Object[] newObjects=new Object[objects.length*2]; //把objects內容拷貝到新的陣列newObjects上 System.arraycopy(objects, 0, newObjects, 0, objects.length);//陣列拷貝函式 objects=newObjects;//之後把newObjects拷貝給objects陣列 } objects[index]=o; index++; } //獲取容器元素個數方法 public int size(){ return index; } }
測試:
package cn.edu.hpu.iterator;
import cn.edu.hpu.iterator.ArrayList;
public class ArrayListTest {
public static void main(String[] args) {
ArrayList al=new ArrayList();
for(int i=0;i<20;i++){
al.add(new Object());
}
System.out.println(al.size());
}
}
測試結果:20我們就完成了這樣一個容器,這個容器是用陣列來實現的。容器比陣列好在哪裡呢?使用容器就不需要考慮到陣列的邊界問題了,容器可以動態拓展。想往裡面裝就add就行了,想知道大小直接size就可以了。
2.我們加一個輔助類Cat
Cat.java:
package cn.edu.hpu.iterator; public class Cat { private int id; public Cat(int id) { super(); this.id = id; } public int getId() { return id; } public void setId(int id) { this.id = id; } }
以後新增元素就新增這個就可以了,因為可以通過id值知道添加了哪些資料
如測試:
package cn.edu.hpu.iterator; import cn.edu.hpu.iterator.ArrayList; public class ArrayListTest { public static void main(String[] args) { ArrayList al=new ArrayList(); for(int i=0;i<20;i++){ al.add(new Cat(i)); } System.out.println(al.size()); } }
3.OK,現在我膩歪了,不想用ArrayList容器了,我想用其它容器去裝Cat,這個新的容器我們底層不用陣列實現了,我們用連結串列來實現。(需要你先了解資料結構中的連結串列概念)
建立一個新容器類LinkedList,一個節點類Node
LinkedList.java:
package cn.edu.hpu.iterator;
public class LinkedList {
Node head=null;//頭節點(以後的元素通過next得到)
Node tail=null;//尾節點
int size=0;
public void add(Object o){
Node n=new Node(o,null);
if(head==null){
head=n;
tail=n;
}
tail.setNext(n);
tail=n;
size++;
}
public int size(){
return size;
}
}
Node.java:
package cn.edu.hpu.iterator;
public class Node {
private Object data;
private Node next;
public Node(Object data, Node next) {
super();
this.data = data;
this.next = next;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
}
測試:
package cn.edu.hpu.iterator;
import cn.edu.hpu.iterator.LinkedList;
public class LinkedListTest {
public static void main(String[] args) {
LinkedList ll=new LinkedList();
for(int i=0;i<20;i++){
ll.add(new Cat(i));
}
System.out.println(ll.size());
}
}
測試結果:20我們實現了兩個內部原理完全不同的容器,那麼下面我們來看:
4.考慮容器的可替換性
什麼叫做可替換性?考慮我們使用容器的客戶端類,想使這些容器可以任意的替換,其它程式碼不用改變,這就需要我們把所有容器的對外提供的方法統一起來。
解決方案:
我們寫一個統一的介面Collection,定義一些統一的規範,讓所有容器去實現這個介面。
Collection.java:
package cn.edu.hpu.iterator;
public interface Collection {
void add(Object o);
int size();
}
ArrayList.java:
package cn.edu.hpu.iterator;
public class ArrayList implements Collection{
Object[] objects=new Object[10];
int index=0;
public void add(Object o){
if(index==objects.length){
Object[] newObjects=new Object[objects.length*2];
System.arraycopy(objects, 0, newObjects, 0, objects.length);
objects=newObjects;
}
objects[index]=o;
index++;
}
public int size(){
return index;
}
}
LinkedList.java:
package cn.edu.hpu.iterator;
public class LinkedList implements Collection{
Node head=null;
Node tail=null;
int size=0;
public void add(Object o){
Node n=new Node(o,null);
if(head==null){
head=n;
tail=n;
}
tail.setNext(n);
tail=n;
size++;
}
public int size(){
return size;
}
}
這樣我們就用Collection介面來實現對不同容器對外方法的統一。
在我們測試的時候就可以使用這種方式來實現容器:
Collection c=new ArrayList();
我對ArrayList不滿意,就可以改成
Collection c=new LinkedList();
小結:針對介面程式設計就不需要考慮具體的實現類是什麼,所以你的程式會更加靈活,要替換其它實現的時候,只需換一個位置(當然如果你寫在配置檔案上,你連程式碼都不用改)。
5.接下來重點來了,我們的容器需要一個非常非常重要的功能-------遍歷
我想遍歷容器中的所有元素,如何去做?
當然可以這樣來寫:
package cn.edu.hpu.iterator;
import cn.edu.hpu.iterator.ArrayList;
import cn.edu.hpu.iterator.Collection;
public class ArrayListTest {
public static void main(String[] args) {
Collection c=new ArrayList();
for(int i=0;i<20;i++){
c.add(new Cat(i));
}
System.out.println(c.size());
ArrayList al=(ArrayList)c;
for(int i=0;i<al.index;i++){
Cat cat=(Cat)al.objects[i];
System.out.print(cat.getId()+" ");
}
}
}
結果:20
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
但是,當我們將Collection的實現改為LinkedList的時候下面的遍歷方法就不行了。
這個時候就需要一種統一的遍歷方式。
每一種容器都應該有它自己的一種遍歷方式,我們要想方設法的給他們統一起來,但是每種容器的遍歷器的實現又不一樣,所以統一遍歷方式只能用一種共同的實現方式,介面或抽象類的方式。
所以我們這麼寫:
定義一個介面Iterator:
package cn.edu.hpu.iterator;
public interface Iterator {
Object next();
boolean hasNext();
}
其中有兩個未實現的方法:next和hasNext。
我要求任何一個容器都必須給我一個實現了這個介面的類的物件,
具體的實現交給具體的容器自己去實現。實現完了之後,我肯定知道,
這個物件就實現這兩個方法了,有了這兩個方法,我就可以實現對這
個容器的遍歷了。
我們在Collection中加入返回Iterator的未實現方法
package cn.edu.hpu.iterator;
public interface Collection {
void add(Object o);
int size();
Iterator iterator();
}
ArrayList實現了iterator()這個方法
public Iterator iterator(){
return null;
}
測試:
package cn.edu.hpu.iterator;
import cn.edu.hpu.iterator.ArrayList;
import cn.edu.hpu.iterator.Collection;
import cn.edu.hpu.iterator.Iterator;
public class ArrayListTest {
public static void main(String[] args) {
Collection c=new ArrayList();
for(int i=0;i<20;i++){
c.add(new Cat(i));
}
System.out.println(c.size());
/*之前的方法
ArrayList al=(ArrayList)c;
for(int i=0;i<al.index;i++){
Cat cat=(Cat)al.objects[i];
System.out.print(cat.getId()+" ");
}*/
//使用統一遍歷器的遍歷方法
ArrayList al=(ArrayList)c;
Iterator it=c.iterator();
while(it.hasNext()){
Object o=it.next();
Cat cat=(Cat)o;
System.out.print(cat.getId()+" ");
}
}
}
這樣我們怎麼改變Collection的實現類,都不影響遍歷方式。6.當然我沒還沒有寫Iterator的實現,我們來寫實現:
我們在ArrayList中寫一個內部類ArrayListIterator,實現Iterator介面:
package cn.edu.hpu.iterator;
public class ArrayList implements Collection{
Object[] objects=new Object[10];
int index=0;
public void add(Object o){
if(index==objects.length){
Object[] newObjects=new Object[objects.length*2];
System.arraycopy(objects, 0, newObjects, 0, objects.length);
objects=newObjects;
}
objects[index]=o;
index++;
}
public int size(){
return index;
}
public Iterator iterator(){
return new ArrayListIterator();
}
private class ArrayListIterator implements Iterator{
private int currentIndex=0;
@Override
public boolean hasNext() {
if(currentIndex>index) return false;
else return true;
}
@Override
public Object next() {
Object o=objects[currentIndex];
currentIndex++;
return o;
}
}
}
測試:
package cn.edu.hpu.iterator;
import cn.edu.hpu.iterator.ArrayList;
import cn.edu.hpu.iterator.Collection;
import cn.edu.hpu.iterator.Iterator;
public class ArrayListTest {
public static void main(String[] args) {
Collection c=new ArrayList();
for(int i=0;i<20;i++){
c.add(new Cat(i));
}
System.out.println(c.size());
//使用統一遍歷器的遍歷方法
ArrayList al=(ArrayList)c;
Iterator it=c.iterator();
while(it.hasNext()){
Object o=it.next();
Cat cat=(Cat)o;
System.out.print(cat.getId()+" ");
}
}
}
測試結果:20
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
在我們的JavaAPI中的ArrayList也實現了Java的Collection介面,同樣提供了一個方法,
叫iterator(),這個方法返回了一個實現了Iterator介面的物件。
現在大家明白iterator()方法到底是幹什麼用的,它返回的介面又是什麼意思了吧?
作業:
這個LinkedList也需要寫一個iterator方法,返回一個實現了Iterator的物件。該如何寫?
這是我自己寫的:
感謝馬士兵老師的"設計模式"視訊提供的幫助