java foreach 循环原理

前言

在jdk1.5以后,出现了foreach循环语法,可用于遍历数组和实现Iterable接口的对象(即迭代器模式),下面将介绍foreach背后具体的实现原理。

常见循环方式

// 基于数组索引引用的循环
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i) + ",");
}

// Iterator迭代器模式循环
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + ",");
}

// foreach循环
for (Integer i : list) {
System.out.print(i + ",");
}

foreach循环原理

通过cmd命令行查看编译后的class文件: javap -c StringList.class

image

或者可以通过可视化工具jclasslib查看

image

说明:通过阅读编译后的class文件,我们可以看出是java编译器隐式的将foreach语法转化为Iterator迭代器模式进行遍历的,对于使用者来说是无感知的,用foreach进行遍历更加方便快捷。并且要求被遍历的对象必须实现Iterable接口。

数组Array没有实现Iterable接口,那么它又是怎么支持foreach循环的呢? 对于数组的foreach循环,编译器是将其转化为对数组中的每一个元素的循环引用,从而达到循环遍历效果。

Iterable源码:

public interface Iterable<T> {

// 返回一个迭代器Iterator对象
Iterator<T> iterator();

default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}

Iterator源码:

public interface Iterator<E> {

boolean hasNext();

E next();

default void remove() {
throw new UnsupportedOperationException("remove");
}

default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}

使用案例

在java中所有实现Collection接口的类(例如:ArrayList,HashSet等),都是可以进行foreach循环遍历的。

public interface Collection<E> extends Iterable<E> {

int size();

boolean isEmpty();

boolean contains(Object o);

Iterator<E> iterator();
......
..........

具体使用案例,参考《java SPI机制原理》中的java.util.ServiceLoader具体实现。

总结

支持foreach循环的对象有以下两种,其他对象使用foreach语法糖,编译器会报错。

  1. 数组;
  2. 实现Iterable接口的类;

参考链接

  1. http://www.hollischuang.com/archives/1776
  2. http://www.importnew.com/11038.html