Java Iterator

Iterator 是用來遍歷集合的介面,提供統一的遍歷方式。

基本使用

List<String> list = Arrays.asList("A", "B", "C");

Iterator<String> it = list.iterator();

while (it.hasNext()) {
    String element = it.next();
    System.out.println(element);
}

Iterator 方法

方法說明
hasNext()是否有下一個元素
next()取得下一個元素
remove()移除當前元素
forEachRemaining()對剩餘元素執行操作

安全地刪除元素

使用 Iterator 可以在遍歷時安全地刪除元素:

List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C", "D"));

// 錯誤:會拋出 ConcurrentModificationException
// for (String s : list) {
//     if (s.equals("B")) {
//         list.remove(s);
//     }
// }

// 正確:使用 Iterator
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    String s = it.next();
    if (s.equals("B")) {
        it.remove();  // 安全刪除
    }
}
// [A, C, D]

forEachRemaining()

Java 8+ 可以對剩餘元素執行操作:

List<String> list = Arrays.asList("A", "B", "C", "D");
Iterator<String> it = list.iterator();

it.next();  // 跳過 "A"

it.forEachRemaining(System.out::println);
// B, C, D

ListIterator

ListIterator 是 List 專用的迭代器,支援雙向遍歷:

List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));

ListIterator<String> it = list.listIterator();

// 正向遍歷
while (it.hasNext()) {
    System.out.println(it.next());
}

// 反向遍歷
while (it.hasPrevious()) {
    System.out.println(it.previous());
}

ListIterator 方法

方法說明
hasPrevious()是否有前一個元素
previous()取得前一個元素
nextIndex()下一個元素的索引
previousIndex()前一個元素的索引
add()新增元素
set()修改當前元素
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
ListIterator<String> it = list.listIterator();

while (it.hasNext()) {
    String s = it.next();
    if (s.equals("B")) {
        it.set("X");  // 修改為 "X"
        it.add("Y");  // 在後面插入 "Y"
    }
}
// [A, X, Y, C]

不同集合的 Iterator

// List
List<String> list = Arrays.asList("A", "B", "C");
Iterator<String> listIt = list.iterator();

// Set
Set<String> set = new HashSet<>(Arrays.asList("A", "B", "C"));
Iterator<String> setIt = set.iterator();

// Map (遍歷 entry)
Map<String, Integer> map = new HashMap<>();
map.put("A", 1);
Iterator<Map.Entry<String, Integer>> mapIt = map.entrySet().iterator();

while (mapIt.hasNext()) {
    Map.Entry<String, Integer> entry = mapIt.next();
    System.out.println(entry.getKey() + ": " + entry.getValue());
}

Iterator vs for-each

List<String> list = Arrays.asList("A", "B", "C");

// for-each(語法糖,內部使用 Iterator)
for (String s : list) {
    System.out.println(s);
}

// Iterator
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    System.out.println(it.next());
}

使用 Iterator 的情況:

  • 需要刪除元素
  • 需要取得索引
  • 需要反向遍歷

自訂 Iterable

讓自訂類別支援 for-each:

public class Range implements Iterable<Integer> {
    private int start;
    private int end;
    
    public Range(int start, int end) {
        this.start = start;
        this.end = end;
    }
    
    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            private int current = start;
            
            @Override
            public boolean hasNext() {
                return current < end;
            }
            
            @Override
            public Integer next() {
                return current++;
            }
        };
    }
}

// 使用
for (int i : new Range(0, 5)) {
    System.out.println(i);
}
// 0, 1, 2, 3, 4

注意事項

ConcurrentModificationException

在遍歷時直接修改集合會拋出例外:

List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));

// 這樣會出錯
for (String s : list) {
    list.add("D");  // ConcurrentModificationException
}

解決方式:

  1. 使用 Iterator 的 remove()
  2. 使用 CopyOnWriteArrayList
  3. 遍歷副本