1. 程式人生 > >[瘋狂Java]集合:Collection的迭代器Iterator、使用Predicate篩選集合中的元素

[瘋狂Java]集合:Collection的迭代器Iterator、使用Predicate篩選集合中的元素

1. Iterator——迭代器:

    1) 和C++中迭代器的概念一樣,二要素:

         i. 迭代器必定從屬於某個容器,其作用就是用來遍歷所屬容器中的元素的!

         ii. 迭代器是在容器的資料檢視之上進行迭代,因此不能再迭代過程中修改容器中的資料,否則會丟擲異常!除非使用迭代器的專用方法對資料進行修改;

    2) Java的迭代器只在Collection中有,而Map沒有迭代器,它有不同的迭代方法;

    3) 迭代器的終極目標:就是用統一的方法來迭代不同型別的集合!可能由於不同集合的內部資料結構不盡相同,如果要自己純手工迭代的話相互之間會有很大的差別,而迭代器的作用就是統一的方法對不同的集合進行迭代,而在迭代器底層隱藏不同集合之間的差異,從而為迭代提供最大的方便;

    4) 用迭代器迭代的步驟:

         i. 第一步肯定是先獲取集合的迭代器:呼叫集合的iterator方法就能獲得,Iterator<E> Collection.iterator(); // 是Collection的物件方法

         ii. 使用迭代器的hasNext、next往下迭代,而hasNext和next在很多地方都見到過,非常熟悉!這就是Java的方便之處;

2. Iterator的常用方法:

    1) boolean hasNext(); // 是否還有下一個元素(肯定有一個位置指標維護者當前迭代的位置)

    2) Object next(); // 取出下一個元素並返回

    3) void remove(); // 從容器中刪除當前元素(即上一個next代表的那個元素),直接會改變容器中的資料!

!!由於Java容器都是泛型類模板,因此容器可以記憶元素的具體型別,因此可以放心使用,只不過取出元素後要進行型別轉換後才能正常使用!

!!!使用迭代器next獲得的元素是一個集合中對應元素的深拷貝(即資料檢視),如果對迭代變數進行修改是不會修改集合中的原資料的!!

!!同樣,也不能直接在迭代過程中使用c.remove等方法對集合進行修改,因為迭代器已經鎖定住集合了,強行修改會丟擲異常!只能用Iterator的專用修改集合元素的方法修改才是正確的,就像上面的Iterator.remove方法;

    4) 模板:

public class Test {
	
	public static void main(String[] args) {
		Collection c = new ArrayList(); // ArrayList是Collection的一個實現類,預設元素型別為Object
		
		Iterator it = c.iterator();
		while (it.hasNext()) {
			Type var = it.next(); // 迭代值(資料檢視)
			對var進行操作;
			c.remove(); // 錯誤!!在迭代過程中使用非迭代器方法對集合進行修改會直接丟擲異常!!
		}
	}
}
!!可以看到,實際上,Iterator迭代的“集合”是真正集合的檢視,檢視和真實資料之間是一一對映的關係,如果此時使用非迭代器方法對真實資料進行修改就會導致真實資料和映像之間不一致,因此會丟擲異常,而迭代器的修改方法可以保證這種對映的一致性,即迭代器先對檢視進行修改,然後將檢視的修改更新到真實資料,但是反向就是無效的,因為映像自己是知道關聯的是哪個真實資料,但是真實資料本身不知道有哪些映像和我關聯的,即真實資料永遠是被動的,而映像是主動的!

3. Lambda表示式結合迭代器進行遍歷——forEachRemaining方法:

    1) Iterator專門提供了一個forEachRemaining方法可以專門使用Lambda表示式進行遍歷,以完成一些強大的功能(同時保證程式碼的高度簡潔);

    2) 原型:default void Iterator.forEachRemaining(Consumer<? super E> action);  // 裡面又是一個函式閉包Consumer action,用來定義對當前迭代變數進行何種操作

    3) 裡面的action同樣是接受每次迭代的變數,相當於上面程式碼"Type var = it.next()“中的var,同樣也是資料檢視,不要在action中用嘗試修改集合中的元素

    4) 示例:it.forEachRemaining(ele -> System.out.println(ele)); // 列印每個元素

4. Java 8新增的Predicate集合過濾方法:

    1) Predicate是一種謂詞動作介面:是一個函式式介面,即表示一個動作

public interface Predicate<T> {
    boolean test(T t);
}
!!即在test中給出一種關於變數t的測試條件;

    2) 該介面主要用來篩選滿足該判斷條件的集合元素;

    3) Java 8為Collection新增了一個支援根據Predicate條件判斷來篩選集合元素的方法:

         i. 該方法是:default boolean Collection.removeIf(Predicate<? super E> filter);  // 可以批量刪除滿足Predicate filter條件的所有元素

         ii. 示例:c.removeIf(ele -> ele.length() < 10); // 批量刪除所有長度小於10的元素

         iii. 目前只增加了一個removeIf,未來可能會增加更多Predicate篩選方法;

5. 靈活運用Predicate:

    1) 畢竟Predicate就是一個函式式介面,你就可以把它當做C語言函式指標使用;

    2) 示例:

public class Test {
	
	public static void operate(Collection c, Predicate p) { // 滿足謂詞條件p的元素都打印出來
		for (Object ele: c) {
			if (p.test(ele)) {
				System.out.println(ele);
			}
		}
	}
	
	public static void main(String[] args) {
		Collection c = new ArrayList();
		for (int i = 0; i < 10; i++) { // 加入0 ~ 9的字串
			c.add(String.valueOf(i));
		}
		
		operate(c, ele -> Integer.valueOf((String)ele) > 3); // 大於3的打印出來
	}
}