為什麼說寫 Java 的人 for循環得用好?

Java 中的循環有很多種,但是什麼情況下用哪種,哪種效率高以及每種的特性,相信大多數人沒有去深究過,這裡面的學問可大著哩,一起來看看吧!

Java 循環的 4 種寫法

注意,是四種寫法,並不是說底層的四種實現方式,這四種寫法各有千秋,但是也是最常用的幾種

  1. 普通的 fori 循環
  2. forEach 語法糖
  3. lambda表達式 forEach
  4. 原生迭代器

注意,以下示例的 User 對象源碼如下:

<code>class User {
        private String name;
        private String address;
        private Integer age;
        public User(String name, String address, Integer age) {
            this.name = name;
            this.address = address;
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getAddress() {
            return address;
        }
        public void setAddress(String address) {
            this.address = address;
        }
        public Integer getAge() {
            return age;
        }
        public void setAge(Integer age) {

            this.age = age;
        }
    }
/<code>

普通 fori 循環

普通 for 循環原理很簡單,首先獲取集合的長度 userList.size(),循環體內根據循環到的下標獲取對應的元素, 然後每次循環 +1,達到遍歷整個集合的目的。

這種寫法在以前非常的常見,現在大多使用 forEach 替代。

<code>List<user> userList = new ArrayList<>();
userList.add(new User("同學1", "北京", 10));
userList.add(new User("同學2", "上海", 15));
userList.add(new User("同學3", "廣州", 12));
// 普通 for 循環
for (int i = 0; i     User user = userList.get(i);
    System.out.println(user);
}
/<user>/<code>

輸出:

<code>User{name='同學1', address='北京', age=10}
User{name='同學2', address='上海', age=15}
User{name='同學3', address='廣州', age=12}
User{name='同學1', address='北京', age=10}
User{name='同學2', address='上海', age=15}
User{name='同學3', address='廣州', age=12}
Process finished with exit code 0
/<code>

但是普通 for 循環有兩個不容忽視的優點。

第一,它在循環過程中可以輕鬆獲取下標,比如我們想在循環中尋找符合條件的下標,那就只能使用 fori 循環,

<code>for (int i = 0; i    User user = userList.get(i);
    if(user.age == 15){
        return i;
    }
}
/<code>

第二點是它並非迭代器實現,也就是說在循環過程中它可以輕鬆的修改集合內的元素,增刪改都沒有問題,雖然不推薦這樣做,但是這樣的需求在實際開發中還是可能遇到。

<code>int size = userList.size();
// 普通 for 循環
for (int i = 0; i     size = userList.size();
    User user = userList.get(i);
    if (user.age == 15) {
        userList.remove(2);
    }
}
/<code>

forEach循環

For-Each 是 Java5 中引入的另一種數組遍歷技術,它以類似於常規for循環的關鍵字開頭具有以下特點:

  1. 無需聲明和初始化循環計數器變量,而是聲明一個與數組的基本類型相同類型的變量,然後是冒號,然後是冒號,然後是數組名。
  2. 在循環主體中,可以使用創建的循環變量,而不是使用索引數組元素。
  3. 它通常用於遍歷數組或Collections類(例如ArrayList)

語法

<code>for (type var : array) 

    statements using var;
}
/<code>

示例

<code>for (int i=0; i<arr.length>{ 
    type var = arr[i];
    statements using var;
}
/<arr.length>/<code>

應用到 fori 的例子

<code>for (User user : userList) {
   System.out.println(user);
}
/<code>

輸出

<code>User{name='同學1', address='北京', age=10}
User{name='同學2', address='上海', age=15}
User{name='同學3', address='廣州', age=12}
User{name='同學1', address='北京', age=10}
User{name='同學2', address='上海', age=15}
User{name='同學3', address='廣州', age=12}
Process finished with exit code 0
/<code>

侷限性:

當你想要在循環體內修改數組時,for-each 循環不合適,你應該選擇普通 fori 循環

<code>for (int num : marks) 
{
    // only changes num, not the array element
    num = num*2; 
}
/<code>

forEach 不跟蹤索引,內部使用迭代器實現,所以我們在循環過程中沒辦法獲取到索引

<code>for (int num : numbers) {
    if (num == target) {
        return ???;   // do not know the index of num
    }
}
For - each only iterates forward over the array in single steps
// cannot be converted to a for-each loop
for (int i = numbers.length - 1; i > 0; i--) {
    System.out.println(numbers[i]);
}
For - each cannot process two decision making statements at once
// cannot be easily converted to a for-each loop
for (int i = 0; i     if (numbers[i] == arr[i]) { ...
    }
}
/<code>

lambda 表達式 forEach

<code>userList.forEach(e -> {
    System.out.println(e);
});
/<code>

這種寫法相比 forEach 更加的簡單,但是存在一個很麻煩的問題,由於 lambda 是基於內部類實現的,所以我們在循環體內如果想修改外部變量,比如這樣

<code>int i = 0;
userList.forEach(e -> {
    System.out.println(e);
    i++;
});

/<code>

代碼中的 i++ 就會報錯,因為內部類無法直接訪問外部資源,Variable used in lambda expression should be final or effectively final,需要我們將變量修改為 Atomic ,如下:

<code>AtomicInteger i = new AtomicInteger();
userList.forEach(e -> {
    System.out.println(e);
    i.set(i.getAndIncrement()+1);
});
/<code>

是不是很蛋疼哩~

迭代器 iterator

迭代器在現在實際開發中使用比較少了,它長這個樣子,其實 forEach 的底層就是迭代器實現。

  • forEach 中對於list編譯器會調用 Iterable 接口的 iterator 方法來循環遍歷數組的元素,iterator方法中是調用Iterator接口的的 next() 和 hasNext() 方法來做循環遍歷。Java 有一個叫做迭代器模式的設計模式,這個其實就是對迭代器模式的一個實現。
  • 對於數組,就是轉化為對數組中的每一個元素的循環引用
<code>Iterator<user> iterator = userList.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}
/<user>/<code>

執行結果

<code>User{name='同學1', address='北京', age=10}
User{name='同學2', address='上海', age=15}
User{name='同學3', address='廣州', age=12}
Process finished with exit code 0
/<code>

好了,關於 Java 中我瞭解的循環的相關內容就講完了,如果對你有幫助,可以關注我,我會不定期發一些個人比較瞭解的技術內容。

ps: 本文中如果您發現錯誤的地方,請私信或者評論指出,感謝!


為什麼說寫 Java 的人 for循環得用好?


分享到:


相關文章: