2. Iterable接口

Iterable接口位于java.lang包下,它表示一个可被遍历的数据结构。该接口提供一个生成Iterator的方法。使用Iterable时无法通过索引获取元素,也无法直接获取首尾元素。

Java中所有集合类都实现了Iterable接口

2.1 遍历Iterable

我们可以使用增强for循环(即for-each循环)遍历集合元素,但只有实现Iterable接口的对象才能用于这种语句。也可以结合Iterator和while语句遍历。

下面是用for-each遍历List的示例:

List<Integer> numbers = getNumbers();
for (Integer number : numbers) {
    System.out.println(number);
}

同样可使用forEach()配合lambda表达式:

List<Integer> numbers = getNumbers();
numbers.forEach(System.out::println);

2.2 实现Iterable接口

当需要遍历自定义数据结构时,实现Iterable接口会很有用。

创建一个表示购物车的类,内部用数组存储元素。我们不直接暴露数组给客户端遍历,而是实现Iterable接口——这样客户端代码不依赖具体数据结构,后续更换实现时无需修改客户端代码

ShoppingCart类实现Iterable接口并重写iterator()方法:

public class ShoppingCart<E> implements Iterable<E> {

    private E[] elementData;
    private int size;

    public void add(E element) {
        ensureCapacity(size + 1);
        elementData[size++] = element;
    }

    @Override
    public Iterator<E> iterator() {
        return new ShoppingCartIterator();
    }
}

add()方法将元素存入数组,通过ensureCapacity()动态扩容。每次调用iterator()都会返回新的Iterator实例——因为迭代器需要维护当前遍历状态

通过实现iterator()方法,就可以用增强for语句遍历该类的对象

现在创建内部类ShoppingCartIterator

public class ShoppingCartIterator implements Iterator<E> {
    int cursor;
    int lastReturned = -1;

    public boolean hasNext() {
        return cursor != size;
    }

    public E next() {
        return getNextElement();
    }

    private E getNextElement() {
        int current = cursor;
        exist(current);

        E[] elements = ShoppingCart.this.elementData;
        validate(elements, current);

        cursor = current + 1;
        lastReturned = current;
        return elements[lastReturned];
    }
}

最后创建实例并使用增强for循环:

ShoppingCart<Product> shoppingCart  = new ShoppingCart<>();

shoppingCart.add(new Product("Tuna", 42));
shoppingCart.add(new Product("Eggplant", 65));
shoppingCart.add(new Product("Salad", 45));
shoppingCart.add(new Product("Banana", 29));
 
for (Product product : shoppingCart) {
   System.out.println(product.getName());
}

3. Iterator接口

Iterator是Java集合框架的成员,位于java.util包。该接口允许在遍历过程中获取或移除集合元素

它包含两个核心方法用于遍历和获取元素:next()hasNext()。此外还有:

  • remove():移除当前指向的元素
  • forEachRemaining(Consumer<? super E> action):对剩余元素执行指定操作

3.1 遍历集合

下面演示如何遍历List<Integer>。我们将结合while循环、hasNext()next()方法。

List接口继承自Collection,因此也实现了Iterable。通过调用iterator()方法获取迭代器:

List<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(20);
numbers.add(30);
numbers.add(40);

Iterator<Integer> iterator = numbers.iterator();

通过hasNext()检查是否有剩余元素,再用next()获取元素:

while (iterator.hasNext()) {
   System.out.println(iterator.next());
}

踩坑提醒next()方法返回下一个元素,若无元素会抛出NoSuchElementException

3.2 实现Iterator接口

自定义迭代器在需要条件性遍历时很有用,例如只遍历奇数或偶数。

这里我们实现一个遍历质数的迭代器。首先创建包含数字集合的类:

class Numbers {
 
    private static final List<Integer> NUMBER_LIST =
      Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
}

然后实现Iterator接口:

private static class PrimeIterator implements Iterator<Integer> {

    private int cursor;

    @Override
    public Integer next() {
        exist(cursor);
        return NUMBER_LIST.get(cursor++);
    }

    @Override
    public boolean hasNext() {
        if (cursor > NUMBER_LIST.size()) {
            return false;
        }

        for (int i = cursor; i < NUMBER_LIST.size(); i++) {
            if (isPrime(NUMBER_LIST.get(i))) {
                cursor = i;
                return true;
            }
        }

        return false;
    }
}

实现要点

  • 通常作为内部类实现
  • 实例变量cursor保存下一个质数的索引
  • 每次next()调用后更新索引

**任何next()实现必须在无元素时抛出NoSuchElementException**,否则可能导致意外行为。

Numbers类中添加获取迭代器的方法:

public static Iterator<Integer> iterator() {
    return new PrimeIterator();
}

最后在while循环中使用自定义迭代器:

Iterator<Integer> iterator = Numbers.iterator();
 
while (iterator.hasNext()) {
   System.out.println(iterator.next());
}

4. Iterable与Iterator的区别

对比维度 Iterable Iterator
核心职责 表示可被for-each遍历的集合 提供遍历集合的能力
实现方法 需重写iterator()方法 需重写hasNext()next()方法
状态维护 不存储遍历状态 存储当前遍历状态
元素移除 遍历过程中不允许移除元素 允许通过remove()移除当前元素

5. 总结

本文深入探讨了Java中IterableIterator接口的核心差异及使用场景。关键要点:

  1. Iterable:让集合支持for-each循环,隐藏内部实现
  2. Iterator:提供细粒度的遍历控制,支持条件遍历和元素移除
  3. 协作关系:Iterable通过iterator()方法生成Iterator实例

简单粗暴地说:Iterable是"可遍历"的声明,Iterator是"怎么遍历"的实现。两者配合使用,既保证代码简洁性又提供遍历灵活性。

示例代码已上传至GitHub仓库


原始标题:Differences Between Iterator and Iterable and How to Use Them? | Baeldung