Itera迭代
- Iterable < T>迭代接口
- (1) Iterator iterator()
- (2) forEach(Consumer<? super T> action)
- forEach結合Consumer常見場景
- forEach使用注意細節
- (3)Spliterator spliterator()
- Iterator< T>迭代器接口
- 如何“接收” Iterator<T>
- 核心方法
- 迭代器的使用場景
- 注意事項
- 總結
在 Java 中, Iterable < T> 是一個核心接口,用于表示一個對象可以被 迭代(遍歷),它定義了如何通過 迭代器(Iterator< T>)訪問其元素,并支持增強的 for-each 循環
Iterable < T>迭代接口
public interface Iterable<T> {Iterator<T> iterator(); // 必須實現的方法// Java 8 新增的默認方法default void forEach(Consumer<? super T> action) { /* ... */ }// Java 8 新增的默認方法default Spliterator<T> spliterator() { /* ... */ }
}
(1) Iterator iterator()
作用:返回一個迭代器(Iterator< T>),用于遍歷集合中的元素或進行刪除元素操作。
實現要求:每個 Iterable 實現類必須提供該方法,返回一個新的迭代器實例
(2) forEach(Consumer<? super T> action)
作用:對集合中的每個非空元素進行遍歷
// 下界通配符 <? super T> 表示“T 或 T 的父類”default void forEach(Consumer<? super T> action) {Objects.requireNonNull(action);for (T t : this) {action.accept(t);}}
首先,這段代碼里的forEach方法接受一個Consumer(消費者)參數,Consumer是一個函數式接口,接受一個輸入參數并且不返回結果,accept方法為void
其次,這里用了泛型通配符<? super T>,表示Consumer可以處理T類型及其父類,比如有一個Animal的Consumer,而T是Dog,那么也可以接受這個Consumer。
步驟1.首先Objects.requireNonNull,確保傳入的action不是null,避免空指針異常,注意是action判空
步驟2.接下來是一個增強的for循環,遍歷this,也就是當前的Iterable實例
步驟3.每次循環取出元素t,調用action的accept方法處理它,具體如何處理 t,完全由 Consumer 的實現邏輯決定,是具體的消費操作action
forEach結合Consumer常見場景
(1) 示例 1:打印元素
List<String> list = Arrays.asList("A", "B", "C");
list.forEach(element -> System.out.println(element));
forEach方法參數是 str -> System.out.println(str) :是一個 Consumer< String> 的實現,action就是打印元素:每次調用 accept(t) 時,會執行 System.out.println(t),即打印當前元素。
(2) 示例 2:修改對象狀態
List<Person> persons = getPersons();
persons.forEach(person -> person.setAge(18)); // 將所有 Person 的年齡設為 18
邏輯分解:
person -> person.setAge(18) 是 Consumer 的實現。每次調用 accept(t) 時,執行 t.setAge(18),修改元素的狀態。
(3) 示例 3:條件判斷與過濾
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
numbers.forEach(num -> {if (num % 2 == 0) {System.out.println(num + " 是偶數");}
});
在 accept(t) 中,根據 t 的值執行條件判斷,僅處理滿足條件的元素。
(4) 示例 4:方法引用
list.forEach(System.out::println); // 方法引用
等價于:
list.forEach(str -> System.out.println(str));
原理:System.out::println 是 Consumer< String> 的實現,accept(t) 會調用 System.out.println(t)
forEach使用注意細節
①上述的判空處理是判斷集合是否或操作是否為空,不是判斷集合中的某個元素為null
②對于Null的元素,foreach遍歷輸出要看具體的子類處理,如List的遍歷是將其作為 null字符打印處理
List<String> list = Arrays.asList("A",null, "C");list.forEach(element -> System.out.println(element));
輸出
A
null //字符null 不是空 Null
C
(3)Spliterator spliterator()
Spliterator可拆分迭代器介紹鏈接
流Stream AP介紹點擊鏈接
Spliterator 是 Stream API 并行處理的底層實現,現在的實際工作場景中,應用流對集合進行處理是 非常常見的,代碼使用是非常簡潔的,這兩個單獨拿出來說
Iterator< T>迭代器接口
Iterator 是 Java 集合框架中用于遍歷集合元素的接口,定義如下:
public interface Iterator<T> {boolean hasNext(); // 檢查是否還有下一個元素T next(); // 返回下一個元素void remove(); // 移除當前元素(可選操作)
}
如何“接收” Iterator
(1) 通過集合的 iterator() 方法
有實現了 Iterable 接口的集合類(如 List, Set, Queue)都可通過 iterator() 方法返回迭代器:
List<String> list = Arrays.asList("A", "B", "C");
Iterator<String> iterator = list.iterator(); // 獲取迭代器
(2) 自定義集合類實現 Iterable<T>
若需讓自定義集合支持迭代,需實現 Iterable 并返回自定義的 Iterator
public class MyCollection<T> implements Iterable<T> {private List<T> elements = new ArrayList<>();public void add(T element) {elements.add(element);}@Overridepublic Iterator<T> iterator() {return new MyIterator(); // 返回自定義迭代器}// 自定義迭代器實現private class MyIterator implements Iterator<T> {private int index = 0;@Overridepublic boolean hasNext() {return index < elements.size();}@Overridepublic T next() {if (!hasNext()) throw new NoSuchElementException();return elements.get(index++);}@Overridepublic void remove() {elements.remove(--index);}}
}
核心方法
(1) boolean hasNext()
作用:判斷集合中是否還有未被遍歷的元素。
返回值:
true:存在下一個元素。
false:已遍歷完所有元素。
List<String> list = Arrays.asList("A", "B", "C");
Iterator<String> it = list.iterator();
while (it.hasNext()) { // 檢查是否還有元素String element = it.next();System.out.println(element);
}
(2) T next()
作用:返回當前指向的元素,并將迭代器位置后移。
注意事項:
①調用前必須檢查 hasNext(),否則可能拋出 NoSuchElementException
②返回類型為泛型 T,確保類型安全
Iterator<Integer> it = List.of(1, 2, 3).iterator();
if (it.hasNext()) {int num = it.next(); // 返回 1,迭代器指向下一個元素
}
(3) void remove()
作用:移除迭代器最后一次通過 next() 返回的元素。
注意事項:
①必須在 next() 之后調用,否則拋出 IllegalStateException
②不是所有迭代器都支持此操作
,如ArrayList的迭代器執行此操作,不支持刪除操作的迭代器如Collections.unmodifiableList()返回的迭代器,調用前需檢查是否實現(默認可能拋出 UnsupportedOperationException)。
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
Iterator<String> it = list.iterator();
while (it.hasNext()) {String element = it.next();if (element.equals("B")) {it.remove(); // 移除 "B"}
}
System.out.println(list); // 輸出 [A, C]
(4) forEachRemaining(Consumer<? super E> action)
上面三個方法是迭代器的核心方法,這個方法是將上面三個方法集合在一起了,他是對父接口的for-each方法的一種補充
default void forEachRemaining(Consumer<? super E> action) {Objects.requireNonNull(action);while (hasNext())action.accept(next());}
和for-each區別就在于,for-each會將集合中的全部元素按照執行操作執行一遍,forEachRemaining則是將剩余未處理的元素按照執行操作執行一遍
,配合可拆分迭代器spliterator中使用樣例
List<String> list = Arrays.asList("Java", "Python", "C++", "Go");
Spliterator<String> spliterator = list.spliterator();// 處理前兩個元素
spliterator.tryAdvance(System.out::println); // Java
spliterator.tryAdvance(System.out::println); // Python// 這時候還剩余兩個元素 批量處理剩余元素
spliterator.forEachRemaining(System.out::println); // 輸出:
// C++
// Go
迭代器的使用場景
(1) 遍歷集合并修改元素
List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3));
Iterator<Integer> it = numbers.iterator();
while (it.hasNext()) {int num = it.next();if (num % 2 == 0) {it.remove(); // 安全刪除偶數}
}
(2) 遍歷不可索引的集合(如 Set)
Set<String> names = new HashSet<>(Set.of("Alice", "Bob"));
Iterator<String> it = names.iterator();
while (it.hasNext()) {System.out.println(it.next());
}
(3) 結合流式處理(Stream API)
這種方式也是實際工作中常見的
List<String> list = List.of("Java", "Python", "C++");
list.stream().filter(s -> s.startsWith("J")).forEach(System.out::println); // 輸出 "Java"
注意事項
①迭代器的三個方法執行有先后順序
boolean hasNext(); // 檢查是否還有下一個元素
T next(); // 返回下一個元素
void remove(); // 移除當前元素(可選操作)
hasNext() 先保證還有下一個元素 ===》next()指針才做指向下一個元素返回---》指向返回元素才能進行操作元素 remove();刪除
②單次遍歷:一個迭代器只能遍歷集合一次,遍歷結束后需重新獲取迭代器。不可重復使用迭代器 即每次調用 iterator() 應返回一個新的迭代器實例。
③并發修改:
List<String> list = new ArrayList<>(Arrays.asList("A", "B"));
Iterator<String> it = list.iterator();
list.add("C"); // 直接修改集合
it.next(); // 拋出 ConcurrentModificationException
解決方案:使用并發集合(如 CopyOnWriteArrayList)或在迭代期間僅通過迭代器修改集合
總結
1.迭代Iterable接口和迭代器Iterator接口 聯系與區別
①關聯:Iterable接口主要生成迭代器和增強for each循環,而增強的 for-each 循環底層依賴 Iterator,無論是在集合還是數組上使用 for-each 循環,其內部都會轉換為相應的迭代器操作,具體的實現方式需要去查看迭代接口的實現類,如ArrayList
//增強for each循環
for (String s : list) {System.out.println(s);
}
//等價于:
Iterator<String> it = list.iterator();
while (it.hasNext()) {String s = it.next();System.out.println(s);
}
②區別:除此之外迭代器提供了一種更為安全的刪除元素的操作方式,即迭代器的三個關鍵方法:hasNext(), next(), remove()配合使用,當僅僅需要遍歷集合使用for-each就可以,但如果需要刪除元素時,可以使用迭代器的 remove() 方法,兩者的主要區別
Iterable<T> | Iterator<T> |
---|---|
表示對象可被迭代 | 實際執行迭代操作 |
定義 iterator() 方法 | 定義 hasNext() , next() , remove() |
支持 for-each 循環 | 手動控制遍歷過程 |
2.集合框架中幾乎所有集合類都實現了 Iterable,例如
- List(如 ArrayList, LinkedList)
- Set(如 HashSet, TreeSet)
- Queue(如 PriorityQueue)
表名這些集合類都是可以被迭代的,當然也都都可以使用迭代器