我们在写程序的时候经常使用for-each来遍历对象。那么为什么有些对象可以通过for-each来遍历呢?我们的自定义类在不继承于集合对象的情况下,是否可以遍历呢?
-
什么是for-each
for-each是增强型的for循环,是java提供的一种语法糖。
把
for (int i = 0; i < list.size; i ++) { Object obj = list.get(i); }
简化成
for (Object obj: list){ }
-
为什么可以被for-each遍历
可以被foreach遍历的对象分两种情况:
-
数组
java对于数组的for-each处理相对简单。只是在编译期把for-each还原成简单的for循环源代码:
Object[] list = new Object[10]; for (Object obj: list){ }
用工具反编译class文件之后的代码:
Object[] list = new Object[10]; Object[] var14 = list; int var15 = list.length; for (int var16 = 0; var16 < var15; ++var16) { Object var10000 = var14[var16]; }
java对于数组的for-each处理相对简单。只是在编译期把for-each还原成简单的for循环
-
实现 Iterable 接口的对象
实现了Iterable的对象,可以被for-each。并且最终java会通过迭代器的形式来遍历它
我们以ArrayList为例(ArrayList实现了Iterable接口)。源代码:
List<Object> list = new ArrayList(); for (Object obj: list){ }
用工具反编译class文件之后的代码:
List<Object> list = new ArrayList(); Object var15; for (Iterator var14 = list.iterator(); var14.hasNext(); var15 = var14.next()) { ; }
实现了Iterable的对象,可以被for-each。并且最终java会通过迭代器的形式来遍历它
-
Iterable 接口
对于ArrayList集合的for-each遍历,java并没有像数组一样简单的还原成for (int i = 0; i < list.size; i ++)
,而是使用了迭代器来遍历集合。这是为什么呢?我们来看看Iterable接口的用途是什么。
/** * Implementing this interface allows an object to be the target of * the "for-each loop" statement. See * <strong> * <a href="{@docRoot}/../technotes/guides/language/foreach.html">For-each Loop</a> * </strong> * * @param <T> the type of elements returned by the iterator * * @since 1.5 * @jls 14.14.2 The enhanced for statement */ public interface Iterable<T> { }
有一段很简单的英文注释:
实现了这个接口(Iterable)允许对象成为for-each的目标。
-
自定义一个可以for-each的类
仿照ArrayList写一个简单的可遍历类。
public class IterableTester<S> implements Iterable<S>{ private Object[] elementData = {}; private int size = 0; public IterableTester(int initialCapacity) { if ( initialCapacity > 0) { elementData = new Object[initialCapacity]; } } /** * 添加方法,返回true/false表明是否成功 */ public boolean add(S element) { if (size < elementData.length) { elementData[size] = element; size++; return true; } else { //超出容量,返回false return false; } } @Override public Iterator<S> iterator() { return new Itr(); } //内部类,实现Interator接口,这样就可以使用迭代器遍历 private class Itr implements Iterator<S> { int cursor; @Override public boolean hasNext() { //如果当前index不等于于size,说明还有下一个 return cursor != size; } @Override public S next() { int i = cursor; if (i >= size) throw new RuntimeException(); Object[] elementData = IterableTester.this.elementData; if (i >= elementData.length) throw new RuntimeException(); cursor = i + 1; return (S) elementData[i]; } } }
这里实现了一个很简单的可遍历类,不考虑扩容等情况。
执行for-each的时候,会首先调用 iterator 方法,返回一个新的迭代器。然后调用迭代器的 hasNext 和 next 方法来进行遍历。接下来我们写一段测试代码,简单跑一下。
IterableTester<String> iterableTester = new IterableTester<>(5); System.out.println(iterableTester.add("1111")); System.out.println(iterableTester.add("2222")); System.out.println(iterableTester.add("3333")); System.out.println(iterableTester.add("4444")); System.out.println(iterableTester.add("5555")); //超出容量,打印出来的是false System.out.println(iterableTester.add("6666")); for (String str: iterableTester) { System.out.println(str); }