Java迭代器案例如何遍历集合

wen java案例 33

Java迭代器案例:如何高效遍历集合?从原理到实战完全指南

📚 目录导读

  1. 什么是Java迭代器? —— 核心概念与设计思想
  2. 迭代器 vs 增强for循环 vs 普通for循环 —— 三大遍历方式对比
  3. Iterator接口详解 —— hasNext()、next()、remove()方法实战
  4. 实战案例:遍历ArrayList、HashSet、HashMap —— 不同集合的迭代器使用
  5. 迭代器常见错误与陷阱 —— ConcurrentModificationException等
  6. Java 8+的迭代器进化 —— 迭代器与Stream API、forEachRemaining()
  7. 高频面试问答 —— 你可能遇到的10个问题

什么是Java迭代器?

迭代器(Iterator) 是Java集合框架中的一种设计模式,它提供了一种统一的方式来遍历集合中的元素,而不需要暴露集合的内部结构,迭代器就像一个“指针”,可以顺序访问集合中的每个元素。

Java迭代器案例如何遍历集合

为什么需要迭代器?

  • 解耦遍历逻辑:无论集合是数组、链表还是哈希表,迭代器提供相同的遍历接口。
  • 支持失败快速机制:当集合被并发修改时,迭代器会抛出ConcurrentModificationException,避免数据不一致。
  • 可删除元素:通过迭代器的remove()方法可以安全地在遍历过程中删除元素。

官方定义java.util.Iterator是Java Collections Framework的核心接口,所有集合类(如List、Set、Queue)都提供了iterator()方法返回迭代器对象。


迭代器 vs 增强for循环 vs 普通for循环

很多开发者会问:“既然有了for-each,为什么还要用迭代器?” 我们用一个表格对比:

遍历方式 适用场景 能否删除元素 是否支持修改 效率
普通for循环 List(有索引) 可以,但易出错 可修改
增强for循环 所有Collection ❌ 不能 不可修改
迭代器 所有Collection ✅ 可以安全删除

核心区别:当需要在遍历的同时删除元素时,必须使用迭代器,使用for-each直接删除会抛出ConcurrentModificationException


Iterator接口核心方法

public interface Iterator<E> {
    boolean hasNext();    // 判断是否还有下一个元素
    E next();             // 返回下一个元素,并将指针后移
    default void remove() { throw new UnsupportedOperationException(); } // 移除当前元素
    default void forEachRemaining(Consumer<? super E> action) { ... } // Java 8新增
}

典型使用模式

Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String element = iterator.next();
    // 处理element
    if ("要删除的元素".equals(element)) {
        iterator.remove();  // 安全删除
    }
}

⚠️ 注意remove()必须在next()之后调用,且每次next()后只能调用一次remove()


实战案例:遍历不同集合

案例1:遍历ArrayList(线性表)

import java.util.*;
public class ArrayListIteratorDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>(Arrays.asList("Java", "Python", "Go", "Rust"));
        // 使用迭代器遍历并删除长度大于3的元素
        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            String lang = it.next();
            if (lang.length() > 3) {
                it.remove();
            }
        }
        System.out.println("删除后: " + list);  // 输出: [Go]
    }
}

案例2:遍历HashSet(无重复元素)

Set<Integer> numbers = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5, 6));
Iterator<Integer> it = numbers.iterator();
while (it.hasNext()) {
    Integer num = it.next();
    if (num % 2 == 0) {
        System.out.print(num + " ");  // 输出偶数: 2 4 6
    }
}

注意:Set迭代器的元素顺序不保证与插入顺序一致,除非使用LinkedHashSet

案例3:遍历HashMap(键值对)

HashMap本身不直接支持迭代器,但可以通过entrySet()获取键值对集合再遍历:

Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 90);
scores.put("Bob", 85);
scores.put("Charlie", 95);
// 方式1:遍历Entry
Iterator<Map.Entry<String, Integer>> entryIt = scores.entrySet().iterator();
while (entryIt.hasNext()) {
    Map.Entry<String, Integer> entry = entryIt.next();
    System.out.println(entry.getKey() + ": " + entry.getValue());
    if (entry.getValue() < 90) {
        entryIt.remove();  // 删除分数低于90的条目
    }
}
// 方式2:遍历Key(不常用)
Iterator<String> keyIt = scores.keySet().iterator();

常见错误与陷阱

陷阱1:在for-each中删除元素

// ❌ 错误代码
for (String s : list) {
    if ("Python".equals(s)) {
        list.remove(s);  // 会抛出ConcurrentModificationException
    }
}
// ✅ 正确做法:使用迭代器
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    if ("Python".equals(it.next())) {
        it.remove();
    }
}

陷阱2:忘记调用next()就调用remove()

Iterator<String> it = list.iterator();
it.remove();  // ❌ IllegalStateException,必须先next()

陷阱3:多线程环境下的并发修改

如果多个线程同时修改集合,建议使用CopyOnWriteArrayListConcurrentHashMap,或者在遍历时加锁。


Java 8+的迭代器进化

forEachRemaining()方法

Java 8为Iterator新增了默认方法,可以一次性处理剩余所有元素:

Iterator<String> it = list.iterator();
it.next();  // 跳过第一个元素
it.forEachRemaining(System.out::println);  // 打印剩余所有元素

迭代器与Stream API

Spliterator(可拆分迭代器)是Stream并行处理的底层基础,我们可以将迭代器转为Stream:

import java.util.stream.StreamSupport;
import java.util.Spliterators;
Iterator<String> it = list.iterator();
Stream<String> stream = StreamSupport.stream(
    Spliterators.spliteratorUnknownSize(it, 0), false);
stream.filter(s -> s.startsWith("J")).forEach(System.out::println);

使用Iterable接口自定义可遍历类

public class MyCollection<T> implements Iterable<T> {
    private List<T> items = new ArrayList<>();
    @Override
    public Iterator<T> iterator() {
        return items.iterator();
    }
    // 增强for循环可以直接使用
    public static void main(String[] args) {
        MyCollection<String> mc = new MyCollection<>();
        for (String s : mc) {  // 自动调用iterator()
            System.out.println(s);
        }
    }
}

高频面试问答(问答形式)

Q1:迭代器与for-each循环有什么区别?

A:for-each本质上是语法糖,底层仍然使用迭代器(编译后会生成Iterator代码),但for-each无法在遍历时删除元素,也无法获取迭代器的位置信息,迭代器更灵活,支持删除和双向遍历(ListIterator)。

Q2:什么是“快速失败”机制?

A:当迭代器遍历集合时,如果集合结构被修改(如增加/删除元素)且不是通过迭代器自身的remove()方法,迭代器会立即抛出ConcurrentModificationException,这是通过维护一个modCount计数器实现的,每次修改集合都会增加该值,迭代器会检查是否匹配。

Q3:如何在遍历时安全删除元素?

A:只能使用迭代器的remove()方法,或者使用Java 8的Collection.removeIf()方法,它底层也是通过迭代器实现的。

Q4:为什么不能在for-each中修改集合?

A:for-each循环在编译后会生成迭代器代码,但你没有迭代器引用,无法调用remove(),而直接调用list.remove()会修改集合的modCount,导致迭代器在下次检查时抛出异常。

Q5:Iterator和ListIterator有什么区别?

AListIterator是Iterator的子接口,专门用于List,它支持:

  • 双向遍历(hasPrevious()和previous())
  • 获取索引(nextIndex()和previousIndex())
  • 修改元素(set()方法)
  • 添加元素(add()方法)

Q6:迭代器遍历HashMap时,顺序是确定的吗?

A:对于普通HashMap,顺序不确定(取决于哈希分布),如果需要有序遍历,使用LinkedHashMap(按插入顺序)或TreeMap(按Key排序)。

Q7:如何遍历一个数组?

A:数组本身没有迭代器,但可以通过Arrays.asList()转为List再获取迭代器,或者直接使用普通for循环。

Q8:forEachRemaining()while(hasNext())哪种更好?

A:功能相同,但forEachRemaining更简洁,适合需要一次性处理剩余所有元素的场景,在并行流处理时,forEachRemaining可能更高效。

Q9:迭代器是fail-safe还是fail-fast?

A:Java集合框架中的迭代器默认是fail-fast(快速失败),但Java也在java.util.concurrent包中提供了并发安全的迭代器(如CopyOnWriteArrayList的迭代器),它们是fail-safe的,允许在遍历时修改集合。

Q10:如何用迭代器实现一个简单的循环?

A:可以使用ListIterator实现双向循环,或者使用Iterator配合while循环,注意检查hasNext()避免无限循环。


Java迭代器是遍历集合的“瑞士军刀”,掌握它能让你:

  • ✅ 安全删除元素
  • ✅ 统一所有集合的遍历方式
  • ✅ 理解并发修改异常的产生原因
  • ✅ 从原理上理解for-each的机制

最佳实践

  • 需要删除元素时 → 使用Iterator
  • 只需要读取元素 → 使用for-each(更简洁)
  • 需要双向遍历或修改 → 使用ListIterator
  • Java 8及以上 → 考虑使用Stream API(更函数式)

扩展阅读Enumeration接口(较老)、Spliterator(用于并行流)、Iterable接口(用于自定义集合)。

抱歉,评论功能暂时关闭!