Java 中的循環有很多種,但是什麼情況下用哪種,哪種效率高以及每種的特性,相信大多數人沒有去深究過,這裡面的學問可大著哩,一起來看看吧!
Java 循環的 4 種寫法
注意,是四種寫法,並不是說底層的四種實現方式,這四種寫法各有千秋,但是也是最常用的幾種
- 普通的 fori 循環
- forEach 語法糖
- lambda表達式 forEach
- 原生迭代器
注意,以下示例的 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循環的關鍵字開頭具有以下特點:
- 無需聲明和初始化循環計數器變量,而是聲明一個與數組的基本類型相同類型的變量,然後是冒號,然後是冒號,然後是數組名。
- 在循環主體中,可以使用創建的循環變量,而不是使用索引數組元素。
- 它通常用於遍歷數組或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: 本文中如果您發現錯誤的地方,請私信或者評論指出,感謝!
閱讀更多 代碼宇宙 的文章