【Java】Iterator 与 Iterable 的区别:从原理到实战

大家好,我是云扬~ 最近在梳理 Java 集合框架的核心知识点时,发现很多小伙伴对 Iterator 和 Iterable 这两个接口的理解有些混淆,甚至会误以为它们是同一个东西。今天就来详细拆解一下这两个接口的区别、核心作用以及实际应用场景,还会附上具体的代码示例,帮助大家彻底搞懂~

一、先明确:两个接口的核心定位

在 Java 集合框架中,这两个接口是实现 “遍历功能” 的基础,但职责完全不同:

  • Iterable 接口:负责 “声明可迭代性”—— 任何类只要实现了这个接口,就意味着它的实例是 “可被遍历” 的,能直接使用 for-each 循环(增强 for 循环)。
  • Iterator 接口:负责 “定义遍历逻辑”—— 它提供了具体的遍历方法(判断下一个元素、获取下一个元素、删除元素),是真正执行遍历操作的 “工具”。

简单说:Iterable 是 “资格证”,有了它才能用 for-each 循环;Iterator 是 “工具人”,负责具体的遍历操作。

二、Iterable 接口:获取遍历 “资格证”

1. 接口定义与核心方法

Iterable 接口的源码非常简洁,核心只有一个方法:

public interface Iterable
    // 返回一个 Iterator 实例,用于后续遍历
    Iterator<T> iterator();
}

所有实现了 Iterable 接口的类,都必须重写 iterator() 方法,返回一个对应的 Iterator 对象。这也是 for-each 循环能工作的关键:当我们用 for-each 遍历一个集合时,底层其实是通过调用 iterator() 方法获取 Iterator 对象,再通过这个对象完成遍历的。

2. 实际应用:自定义类实现 Iterable

比如我们想创建一个 “自定义书架类”,让它能直接用 for-each 循环遍历书架上的书。步骤如下:

import java.util.Iterator;

// 自定义书架类,实现 Iterable 接口(声明可迭代)
public class BookShelf implements Iterable<String> {
    private String[] books;
    private int size;

    // 构造方法:初始化书架
    public BookShelf(int capacity) {
        books = new String[capacity];
        size = 0;
    }

    // 添加书籍
    public void addBook(String book) {
        if (size < books.length) {
            books[size++] = book;
        }
    }

    // 核心:实现 Iterable 接口的 iterator() 方法,返回 Iterator 实例
    @Override
    public Iterator<String> iterator() {
        // 返回自定义的 Iterator 实现类(匿名内部类形式)
        return new Iterator<String>() {
            private int index = 0; // 遍历指针

            // 判断是否还有下一个元素
            @Override
            public boolean hasNext() {
                return index < size;
            }

            // 获取下一个元素,并移动指针
            @Override
            public String next() {
                return books[index++];
            }
        };
    }

    // 测试:直接用 for-each 循环遍历书架
    public static void main(String[] args) {
        BookShelf shelf = new BookShelf(3);
        shelf.addBook("《Java 编程思想》");
        shelf.addBook("《深入理解 Java 虚拟机》");
        shelf.addBook("《Effective Java》");

        // 因为 BookShelf 实现了 Iterable,所以可以直接用 for-each 循环
        for (String book : shelf) {
            System.out.println("书架上的书:" + book);
        }
    }
}

运行结果:

书架上的书:《Java 编程思想》
书架上的书:《深入理解 Java 虚拟机》
书架上的书:《Effective Java》

可以看到,只要实现了 Iterable 接口并提供 Iterator 实例,自定义类就能无缝支持 for-each 循环,这就是 Iterable 接口的核心价值。

三、Iterator 接口:实现具体的遍历逻辑

1. 接口定义与核心方法

Iterator 接口定义了遍历的三大核心操作,源码如下:

public interface Iterator<E> { 
   // 判断集合中是否还有未遍历的元素
    boolean hasNext();

    // 返回下一个元素,并将遍历指针向后移动一位
    T next();

    // 删除上一次 next() 方法返回的元素(可选操作,需谨慎使用)
    default void remove() {
        throw new UnsupportedOperationException("remove");
    }
}

三个方法的核心作用:

  • hasNext():防止越界访问,遍历前先判断是否还有元素。
  • next():获取当前元素,并移动指针(注意:必须先调用 hasNext() 判断,否则可能抛出 NoSuchElementException)。
  • remove():删除上一次 next() 获取的元素(是唯一安全的集合遍历删除方式,避免 ConcurrentModificationException)。

2. 实际应用:用 Iterator 遍历集合并删除元素

比如我们有一个 List 集合,需要遍历并删除其中的偶数:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class IteratorDemo {
    public static void main(String[] args) {
        List<Integer> numList = new ArrayList<>();
        numList.add(1);
        numList.add(2);
        numList.add(3);
        numList.add(4);
        numList.add(5);

        // 1. 获取 Iterator 对象
        Iterator<Integer> iterator = numList.iterator();

        // 2. 遍历并删除偶数
        while (iterator.hasNext()) {
            Integer num = iterator.next(); // 先获取元素
            if (num % 2 == 0) {
                iterator.remove(); // 安全删除(直接调用 Iterator 的 remove 方法)
            }
        }

        // 输出结果:[1, 3, 5]
        System.out.println("删除偶数后的集合:" + numList);
    }
}

这里要注意:不能在 for-each 循环中直接调用集合的 remove () 方法(会触发并发修改异常),而通过 Iterator 的 remove() 方法删除是安全的,因为它会同步更新集合的修改次数。

四、核心区别总结

为了让大家更清晰地对比,整理了一张表格:

特性Iterable 接口Iterator 接口
核心职责声明类的可迭代性,支持 for-each 循环定义具体的遍历逻辑(判断、获取、删除)
核心方法iterator():返回 Iterator 实例hasNext()next()remove()
依赖关系依赖 Iterator 接口(返回其实例)独立工作,无需依赖 Iterable
使用场景让自定义类支持 for-each 循环遍历集合、安全删除元素

五、实际开发中的注意事项

  1. for-each 循环的本质:底层是 Iterator 的封装,所以能遍历所有实现了 Iterable 接口的类(如 List、Set、Collection 等),但 Map 没有实现 Iterable,所以不能直接用 for-each 遍历(需通过 keySet()entrySet() 转换)。
  2. Iterator 的单次遍历限制:一个 Iterator 实例只能遍历一次集合,若需再次遍历,需重新调用 iterator() 方法获取新实例。
  3. remove () 方法的使用限制:必须在调用 next() 之后才能调用 remove(),且每次 next() 只能对应一次 remove(),否则会抛出 IllegalStateException

最后

Iterator 和 Iterable 是 Java 集合遍历的基础,理解它们的职责分工,能帮助我们更灵活地使用集合框架,甚至自定义可遍历的类。希望这篇文章能帮大家彻底搞懂两者的区别,下次遇到相关问题再也不会混淆啦~

如果有疑问或补充,欢迎在评论区留言交流~ 另外,之前还分享过 ArrayList、LinkedList 等集合的底层原理和实战技巧,感兴趣的小伙伴可以去看看哦~

Tags:

发表回复

Your email address will not be published. Required fields are marked *.

*
*