JAVA for-each原理

我們在寫程序的時候經常使用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遍歷的對象分兩種情況:

  1. 數組
    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循環

  2. 實現 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 方法,返回一個新的迭代器。然後調用迭代器的 hasNextnext 方法來進行遍歷。

    接下來我們寫一段測試代碼,簡單跑一下。

    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);
    }
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章