我們在寫程序的時候經常使用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); }