1. 程式人生 > >從原始碼角度看for迴圈和foreach的區別

從原始碼角度看for迴圈和foreach的區別

for迴圈和foreach的區別

關於for迴圈和foreach的區別,你真的知道,用了那麼多年使用起來已經很熟悉了,可突然問我講講這兩的區別,一下還真把我給卡住了一下,下面從原始碼的角度簡單分析一下吧;

for迴圈的使用

for迴圈通過下標的方式,對集合中指定位置進行操作,每次遍歷會執行判斷條件 i<list.size(),滿足則繼續執行,執行完一次i++;
for(int i=0;i<list.size();i++)
{
    System.out.println(i + ":" + list.get(i));
}
也就是說,即使在for迴圈中對list的元素remove和add也是可以的,因為新增或刪除後list中元素個數變化,繼續迴圈會再次判斷i<list.size();  也就是說list.size()值也發生了變化,所以是可行的,具體操作如下程式碼

for (int i = 0; i < list.size(); i++) {
   if (i == 3) {
    list.add("中間插入的一個字串");
   }
   if (i == 5) {
    {
     list.remove(6);
    }
   }
   System.out.println(i + ":" + list.get(i));
  }

增強for迴圈:foreach迴圈的原理

同樣地,使用foreach遍歷上述集合,注意foreach是C#中的寫法,在Java中寫法依然是for (int i : list) 寫法for(String str : list)
檢視文件可知,foreach除了可以遍歷陣列,還可以用於遍歷所有實現了Iterable<T>介面的物件 用普通for迴圈的方式模擬實現一個foreach,由於List實現了Iterable<T>, 過程如下:首先通過iterator()方法獲得一個集合的迭代器,然後每次通過遊標的形式依次判斷是否有下一個元素,如果有通過 next()方法則可以取出。 注意:執行完next()方法,遊標向後移一位,只能後移,不能前進。  用傳統for迴圈的方式模擬 增強for迴圈
和for迴圈的區別在於,它對索引的邊界值只會計算一次。所以在foreach中對集合進行新增或刪掉會導致錯誤,丟擲異常java.util.ConcurrentModificationException

private static void testForeachMethod(ArrayList<String> list) {
  int count = 0; // 記錄index
  for (String str : list) {
   System.out.println(str);
   count++;
   if (count == 3) {
    // foreach中修改集合長度會丟擲異常
    // list.add("foreach中插入的ABC");
   }
  }
 }


具體可以從原始碼的角度進行理解

1.首先是呼叫iterator()方法獲得一個集合迭代器


初始化時  expectedModCount記錄修改後的個數,當迭代器能檢測到expectedModCount是否有過修改

在建立迭代器之後,除非通過迭代器自身的 remove 或 add 方法從結構上對列表進行修改,否則在任何時間以任何方式對列表進行修改,迭代器都會丟擲 ConcurrentModificationException。因此,面對併發的修改,迭代器很快就會完全失敗,而不是冒著在將來某個不確定時間發生任意不確定行為的風險。

注意,迭代器的快速失敗行為無法得到保證,因為一般來說,不可能對是否出現不同步併發修改做出任何硬性保證。快速失敗迭代器會盡最大努力丟擲 ConcurrentModificationException。因此,為提高這類迭代器的正確性而編寫一個依賴於此異常的程式是錯誤的做法:迭代器的快速失敗行為應該僅用於檢測 bug。


相關推薦

原始碼角度for迴圈foreach區別

for迴圈和foreach的區別 關於for迴圈和foreach的區別,你真的知道,用了那麼多年使用起來已經很熟悉了,可突然問我講講這兩的區別,一下還真把我給卡住了一下,下面從原始碼的角度簡單分析一

原始碼角度php自增自減】

自增和自減基礎 學過程式語言的同學應該都可以隨口說出 ++a 和 a++ 的區別,具體的區別如下: Example Name Effect ++$a Pre-increment Increments abyone,then

原始碼角度Spring生命週期(官方最全)

Spring在beanfactory中給出了spring的生命週期的list列表 一、bean初始化前的處理 Bean factory implementations should support the standard bean lifecycle interfaces as

for迴圈foreach區別

//定義一個list集合 List<String> list=new ArrayList<String>(); list.add("aaa"); list.add("bbb"); list.add("ccc"); list.add(

Java流程控制的陷阱——for迴圈foreach迴圈的陷阱

5、for迴圈的陷阱 5、1 分號的問題 public class SemicolonRight { public static void main(String[] args) { for ( int j = 1 , i = 0 ; i < 5 &&

【Android】原始碼角度Handler機制

在Android開發規範中,規定了主執行緒的任務的響應時間不能超過5s,否則會出現ANR,即程式無響應。為了避免這個問題的出現,常用的一個解決方案就是開闢新執行緒,在開闢出來的子執行緒中去處理耗時的業務,然後回到UI執行緒(主執行緒)來重新整理UI,這個過程中“

使用角度 ReentrantLock Condition

阻塞 transient string turn his 介紹 ner await dex java 語言中談到鎖,少不了比較一番 synchronized 和 ReentrantLock 的原理,本文不作分析,只是簡單介紹一下 ReentrantLock 的用法,從使用中

template到DOM(Vue.js原始碼角度內部執行機制)

寫在前面 這篇文章算是對最近寫的一系列Vue.js原始碼的文章(https://github.com/answershuto/learnVue)的總結吧,在閱讀原始碼的過程中也確實受益匪淺,希望自己的這些產出也會對同樣想要學習Vue.js原始碼的小夥伴有所幫助。之前這篇文章同樣在我司(大搜車)的

02 JDK原始碼角度Boolean

Java的Boolean類主要作用就是對基本型別boolean進行封裝,提供了一些處理boolean型別的方法,比如String型別和boolean型別的轉換。 主要實現原始碼如下圖所示,具體實現程式碼可自行檢視對應的程式碼。 既然是對基本型別boolean的封裝,那

JDK 原始碼角度 Object

Java的Object是所有其他類的父類,從繼承的層次來看它就是最頂層根,所以它也是唯一一個沒有父類的類。它包含了物件常用的一些方法,比如getClass、hashCode、equals、clone、toString、notify、wait等常用方法。所以其他類繼承了Obje

另一個角度事件驅動協程

有一些新的想法,可能大部分的軟體架構就是人的活動的一種抽象。比如事件驅動,就可以從人的日常活動中提煉出來,設想一個禮拜天你在家裡打遊戲/上網,這個時候你媽媽打電話過來說外面下雨了,讓你去收衣服,這就是一個典型的事件驅動,你這個時候去收衣服,就是執行了回撥函式,如果不去收繼續打遊戲,就是出了B

微服務治理的角度RSocket,. Envoy. Istio

很多同學看到這個題目,一定會提這樣的問題:RSocket是個協議,Envoy是一個 proxy,Istio是service mesh control plane + data plane。 這三種技術怎麼能放在一起比較呢? 的確,從技術定位的角度來講,它們確實是有很大的差距。但是,如果我們用

彙編for迴圈

從彙編看for迴圈 看看在彙編中for迴圈是怎樣來實現的。 寫一個簡短的C語言程式碼: int main() { int i; for(i=0;i<10;i++) { printf("%d /n",i); } return 0; } 照例

Vue.js原始碼解析(九)【template到DOM(Vue.js原始碼角度內部執行機制)】

從new一個Vue物件開始 let vm = new Vue({ el: '#app', /*some options*/ }); 很多同學好奇,在new一個Vue物件的時候,內部究竟發生了什麼? 究竟Vue.js是如何將data中的資

Vue學習之原始碼分析--template到DOM(Vue.js原始碼角度內部執行機制)(九)

從new一個Vue物件開始 let vm = new Vue({ el: '#app', /*some options*/ }); 很多同學好奇,在new一個Vue物件的時候,內部究竟發生了什麼? 究竟Vue.js是如何將data中的資

原始碼角度深入理解iScroll中的scrollbarsindicators配置

問題1:在IScroll中都是使用同樣的方法對scrollbars和indicators進行初始化 if ( this.options.scrollbars || this.options.indicators ) { this._initIndicators();

Android View 繪製流程 與invalidate postInvalidate 分析--原始碼角度

整個View樹的繪製流程是在ViewRootImpl.java類的performTraversals()函式展開的,該函式做的執行過程可簡單概況為  根據之前設置的狀態,判斷是否需要重新計算檢視大小(measure)、是否重新需要佈局檢視的位置(layout

模運算的角度原碼補碼

# 從模運算的角度看原碼和補碼 > 寫作的背景:之前在學習計算機基礎的過程當中,對於計算機原碼、反碼和補碼的相關知識一直處在一知半解的狀態,即僅僅只停留在會用的階段,但是對於計算機中引入補碼的原因,以及補碼是怎麼來的(從數學的角度看)類似這樣的問題自己一直處於懵逼狀態。雖然老師也曾經對此作出過解釋,但

狀態模式“大神”“菜鳥”的差別

err pri after dsm ets 大話設計模式 post else clas 《大話設計模式》中講狀態模式這一節名字叫做“無盡加班何時休-狀態模式”。菜鳥因為編程經驗不足,解決這個問題的能力不夠而不得不犧牲時間去不斷的寫代碼結果還是重復出錯。而大神

Javascript-關於for inforEach

style 工程師 數組 log foreach function name gpo func JS-for in:用來遍歷對象 1 //遍歷對象 for in 2 3 var opts={name:‘xiaofei‘,age:‘28歲‘,job:‘web前端工程