Java增强for循环的底层实现原理与自定义Iterable实战
在Java集合框架的顶层设计中,Collection接口继承了Iterable接口,这正是各类集合能够支持for-each语法的关键所在。要探究其底层逻辑,必须从Iterable入手。
1. 顶层接口 Iterable 的职责
Iterable接口的核心作用是赋予对象被遍历的能力。它定义了一个核心方法iterator(),其余方法如forEach均提供了默认实现,暂不深究。
public interface Iterable<T> {
Iterator<T> iterator();
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
}
而在Iterator接口中,最关键的两个抽象方法是boolean hasNext()与T next(),分别用于判断是否存在后继元素以及获取下一个元素。
2. 语法糖:增强for循环的本质
日常开发中编写的增强for循环,实际上只是Java编译器提供的一种语法糖。在编译阶段,它会被解糖为基于Iterator的标准循环。
原始代码:
public static void iterateWithForEach(ArrayList<String> dataList) {
for (String item : dataList) {
System.out.println(item);
}
}
编译后的反编译代码:
public static void iterateWithForEach(ArrayList<String> dataList) {
Iterator<String> iter = dataList.iterator();
while(iter.hasNext()) {
String item = iter.next();
System.out.println(item);
}
}
显而易见,增强for循环的底层完全依赖于迭代器的hasNext()和next()方法驱动。
3. ArrayList 中的迭代器实现机制
在ArrayList内部,iterator()方法直接实例化并返回了一个私有内部类Itr。
public Iterator<E> iterator() {
return new Itr();
}
Itr内部类维护了三个核心状态变量:游标cursor(下个待返回元素的索引)、上次返回的索引lastRet,以及期望的修改次数expectedModCount。重写后的核心逻辑如下:
private class ArrayIterator implements Iterator<E> {
int nextIndex = 0;
int prevIndex = -1;
int expectedChanges = modCount;
public boolean hasNext() {
return nextIndex != size;
}
public E next() {
verifyModification();
int current = nextIndex;
if (current >= size)
throw new NoSuchElementException();
Object[] data = ArrayList.this.elementData;
if (current >= data.length)
throw new ConcurrentModificationException();
nextIndex = current + 1;
return (E) data[prevIndex = current];
}
final void verifyModification() {
if (modCount != expectedChanges)
throw new ConcurrentModificationException();
}
}
hasNext()通过比较当前游标与集合大小来判断是否还有后续元素。而next()方法首先调用verifyModification()检查结构是否被并发修改。如果遍历期间通过非迭代器方式修改了集合,modCount将不再等于expectedChanges,从而抛出ConcurrentModificationException(即Fail-Fast机制)。校验通过后,更新游标位置并返回底层数组对应的元素。
4. 为自定义类型赋予for-each能力
理解了上述机制,我们就可以让自己的类实现Iterable接口,从而使其能够直接应用于增强for循环。
public class Product implements Iterable<String> {
private String productId;
private String productName;
private double price;
@Override
public Iterator<String> iterator() {
return new ProductAttributeIterator();
}
private class ProductAttributeIterator implements Iterator<String> {
private int step = 0;
private final int totalFields = 3;
@Override
public boolean hasNext() {
return step < totalFields;
}
@Override
public String next() {
return switch (step++) {
case 0 -> productId != null ? productId : "N/A";
case 1 -> productName != null ? productName : "N/A";
case 2 -> String.valueOf(price);
default -> throw new NoSuchElementException();
};
}
}
}