第十一篇:迭代器模式

迭代器模式其實還是非常好理解的,因爲我們無時無刻都在使用着迭代器模式,當我們用foreach語法遍歷List集合,Set集合的,Map集合的時候,無形中就用到了java實現的迭代器,比如這樣:

	public static void main(String[] args) {
		List<String> list = new ArrayList<String>(Arrays.asList("張三","李四","王五"));
		for (String s : list) {
			System.out.print(s+",");
		}
		
		System.out.println();
		//foreach實現原理相當於直接使用下面的代碼
		Iterator<String> iterator = list.iterator();
		while( iterator.hasNext() ){
			String s = iterator.next();
			System.out.print(s+",");
		}
	}

現在我們知道了在遍歷List的集合的時候,是先通過調用它的iterator()方法拿到迭代器,再去藉助於迭代器進行數據遍歷, 接下來就去看看Iterator接口有些什麼!

/**
 * An iterator over a collection.  {@code Iterator} takes the place of
 * {@link Enumeration} in the Java Collections Framework.  Iterators
 * differ from enumerations in two ways:
 */
public interface Iterator<E> {
	
	//是否有下一個元素
    boolean hasNext();

	//取出下一個元素
    E next();

	//移除一個元素
    void remove();
}

我們去看源碼可以發現,Set,List接口都間接繼承了Iterable接口,所以所有的List和Set集合都提供了Iterator()方法用來獲取迭代器,正因如此,編譯器在解析foreach語法的時候,就可以放心的轉換成調用要遍歷的對象的iterator()方法來進行遍歷了;

那麼java爲什麼要設計一個迭代器來進行數據遍歷呢?爲什麼不直接在具體實現類中直接提供遍歷方法呢? 我們用代碼來說話吧,對比不用迭代器和使用迭代器的區別,你就完全明白了!


場景假定: 假設我們的學校現在有兩個班級,而某一天兩個班級合併了,此時需要清點人數...我們先來看一看代碼

/**高年級一班,一班的學生是用數組來存儲的,爲了代碼簡介,
 	我就不寫增加學生方法...移除學生方法...
 	也不去建立一個學生類了,直接用String代表一個學生!
 */
public class GradeOne{
	
	private String [] students; 
	
	public GradeOne() {
		int length = 5;
		students = new String[length];
		for(int i = 0 ; i < length ; i++){
			students[i] = "李尋歡"+i;
		}
	}
	//提供獲取學生的方法
	public String[] getStudents() {
		return students;
	}
}

/**年級二班是用List來存儲學生的*/
class GradeTwo {
	
	private List<String> students;
	
	public GradeTwo() {
		students = new ArrayList<String>();
		for (int i = 0; i < 8; i++) {
			students.add("喬峯"+i);
		}
	}
	public List<String> getStudents() {
		return students;
	}
}



/**老師類*/
class Teacher{
	
	/***
	 * 該老師要清楚的知道自己班級中的所有人,並能說出來
	 */
	public void print(){
		//此時此刻,由於先前三個班級的代碼是不同的人寫的,
		//所以擁有不同的數據結構,這時候老師就有點痛苦了...
		//我們接着往下看...
		GradeOne gradeOne = new GradeOne();
		String [] oneStudents = gradeOne.getStudents();
		for (int i = 0; i < oneStudents.length; i++) {
			System.out.print( oneStudents[i]+"," );
		}
		
		GradeTwo gradeTwo = new GradeTwo();
		List<String> twoStudents = gradeTwo.getStudents();
		for (int i = 0; i < twoStudents.size(); i++) {
			System.out.print( twoStudents.get(i)+"," );
		}
	}
}


我們看看上面的Teacher類代碼,老師要對原本兩個班級合併成的一個班級裏的人數進行清點,但卻發現開發這兩個個班級類的程序猿所使用的數據結構都不相同,所以老師類只好對每種結構進行不同方式的遍歷...如果繼續有不同數據結構的班級融入進來...代碼又得改!老師類希望的是,它不用去管底層f封裝好的數據結構,它急切希望有一種能對各種數據結構都能進行遍歷的統一的方法!由此,迭代器模式誕生!

讓我們來改動一下代碼吧!

先抽象增加迭代器接口:

/**一個遍歷數據統一的接口*/
interface Iterator<E>{
	//是否有下一個元素
	boolean hasNext();
	
	//取得下一個元素
	E next();
}

/**一旦實現此接口,代表是支持迭代器遍歷的*/
interface Iterable<E>{
	Iterator<E> iterator();
}


繼續改動班級代碼,讓其實現Iterable接口,且去掉getStudents()方法,對外界屏蔽內部數據結構實現,使其保持高內聚;

/**高年級一班,一班的學生是用數組來存儲的,爲了代碼簡介,
 	我就不寫增加學生方法...移除學生方法...
 	也不去建立一個學生類了,直接用String代表一個學生!
 */
public class GradeOne implements Iterable<String>{
	private String [] students; 
	public GradeOne() {
		int length = 5;
		students = new String[length];
		for(int i = 0 ; i < length ; i++){
			students[i] = "李尋歡"+i;
		}
	}
	//用匿名內部類實現返回一個迭代器
	@Override
	public Iterator<String> iterator() {
		return new Iterator<String>() {
			//記錄當前讀取的元素下標
			private int currentIndex = 0;
			@Override
			public boolean hasNext() {
				return currentIndex<students.length;
			}
			@Override
			public String next() {
				return students[currentIndex++];
			}
		
		};
	}
}

/**年級二班是用List來存儲學生的*/
class GradeTwo implements Iterable<String>{
	
	private List<String> students;
	
	public GradeTwo() {
		students = new ArrayList<String>();
		for (int i = 0; i < 8; i++) {
			students.add("喬峯"+i);
		}
	}
	@Override
	public Iterator<String> iterator() {
		return new Iterator<String>() {
			private int currentIndex = 0;
			@Override
			public boolean hasNext() {
				return currentIndex<students.size();
			}
			@Override
			public String next() {
				return students.get(currentIndex++);
			}
		
		};
	}
}

/**老師類*/
class Teacher{
	/***
	 * 該老師要清楚的知道自己班級中的所有人,並能說出來
	 */
	public void print(){
		GradeOne gradeOne = new GradeOne();
		GradeTwo gradeTwo = new GradeTwo();
		print(gradeOne.iterator());
		print(gradeTwo.iterator());
	}
	
	public void print( Iterator iterator ){
		while( iterator.hasNext() ){
			System.out.println( iterator.next() );
		}
	}
}


測試一下:

public class Test {
	public static void main(String[] args) {
			Teacher teacher = new Teacher();
			teacher.print();
	}
}


輸出:

*******************************************************************

李尋歡0,李尋歡1,李尋歡2,李尋歡3,李尋歡4,喬峯0,喬峯1,喬峯2,喬峯3,喬峯4,喬峯5,喬峯6,喬峯7,

*******************************************************************

上面就是我們自己實現的迭代器,當然它很簡陋,但足以用來說清迭代器模式了..感興趣可以去看看java中List,Set等等集合的迭代器實現方式; 

我們對比兩斷代碼發現,實現了迭代器後,代碼變得簡潔,無論底層是什麼數據結構,客戶端都能用一致的方法來進行遍歷了!所以我們可以得出結論,迭代器模式的作用就是爲所有不同數據結構的集合提供一個統一的遍歷方式!



發佈了48 篇原創文章 · 獲贊 5 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章