自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

For 循環(huán)用了那么多次,但你真的了解它么?

開發(fā) 前端
其實(shí)我們寫代碼的時(shí)候一直都在使用for循環(huán),但是偶爾還是會(huì)糾結(jié)用哪一個(gè)循環(huán)。

 其實(shí)我們寫代碼的時(shí)候一直都在使用for循環(huán),但是偶爾還是會(huì)糾結(jié)用哪一個(gè)循環(huán)。

[[285942]]

一、基礎(chǔ)的for循環(huán)

1、使用while也是一種循環(huán)方式,此處探究for相關(guān)的循環(huán),就不做拓展了。

2、遍歷數(shù)組的時(shí)候,初學(xué)時(shí)是使用的如下樣式的for循環(huán):

  1. for(int i=0;i<a.length;i++){ 
  2.     System.out.println(n); 

 

3、而遍歷集合的時(shí)候使用的都是Iterator迭代器:

給定一組人名,兩兩組隊(duì)(此處允許自己和自己組隊(duì)),實(shí)現(xiàn)如下:

 

  1. enum Option {Tom, Jerry, Jack, Mary} 

想象中的寫法是:

  1. Collection<Option> options = Arrays.asList(Option.values()); 
  2. for(Iterator<Option> i = options.iterator(); i.hasNext();){ 
  3.     for (Iterator<Option> j = options.iterator(); j.hasNext();) { 
  4.         System.out.println(i.next()+" "+j.next()); 
  5.     } 

但是執(zhí)行過后你會(huì)發(fā)現(xiàn)這段代碼是有瑕疵的,出現(xiàn)的結(jié)果只有四組:

 

那么剩下的組合去哪里了呢?

這里程序并不會(huì)拋出異常,只是單純的因?yàn)閕.next()每次都會(huì)取下一個(gè)值,所以就出現(xiàn)了上圖的情況。

但是,如果外部集合的元素大于內(nèi)部元素:

例如下面這段代碼:

  1. enum OptionFirst {Tom, Jerry, Jack, Mary} 
  2. enum OptionSecond {Tom, Jerry, Jack, Mary, Mali, Tomsun, Lijie, Oolyyou} 
  3.  
  4. public static void main(String[] args) { 
  5.     Collection<OptionFirst> optionFirstCollection = Arrays.asList(OptionFirst.values()); 
  6.     Collection<OptionSecond> optionSecondCollection = Arrays.asList(OptionSecond.values()); 
  7.     for (Iterator<OptionFirst> i = optionFirstCollection.iterator(); i.hasNext(); ) { 
  8.         for (Iterator<OptionSecond> j = optionSecondCollection.iterator(); j.hasNext(); ) { 
  9.             System.out.println(i.next() + " " + j.next()); 
  10.         } 
  11.     } 

運(yùn)行后,就會(huì)拋出java.util.NoSuchElementException異常,造成的原因就是因?yàn)橥獠垦h(huán)調(diào)用了多次,而內(nèi)部循環(huán)因?yàn)樵夭蛔?,?dǎo)致循環(huán)拋出了這樣的異常。

要想解決這種困擾只需要在二次循環(huán)前添加一個(gè)變量來保存外部元素;即可實(shí)現(xiàn)想要達(dá)到的效果。

  1. Collection options = Arrays.asList(Option.values()); 
  2.  
  3. for(Iterator i = options.iterator(); i.hasNext();){ 
  4.  
  5. Option option = i.next(); 
  6.  
  7. for (Iterator j = options.iterator(); j.hasNext();) { 
  8.  
  9. System.out.println(option+" "+j.next()); 
  10.  
  11.  

二、for-each循環(huán)

這種循環(huán)方式不論是數(shù)組還是集合都實(shí)用,而且效率更高;表達(dá)形式更加簡潔明了。

 

  1. for(Element e:elements){ 
  2.     System.out.println(e); 

當(dāng)再次遇到上面的兩兩組隊(duì)問題時(shí),根本不需要考慮元素不足的問題,而且代碼也簡潔多了:

 

  1. for (Option option : options) { 
  2.     for (Option rank : options) { 
  3.         System.out.println(option + " " + rank); 
  4.     } 

《Effective Java》中是這樣子寫for-each循環(huán)的:

 

 

 

 

三、for-each is not god

說了for-each那么多好處,但是它也不是神,并非萬能的,有那么三種情況是它需要注意的。

3.1、解構(gòu)過濾的時(shí)候不能使用

如果需要遍歷集合,并刪除選定的元素,就需要使用顯式的迭代器,以便可以調(diào)用它的remove方法。不過在Java8中增加的Collection的removeIf方法常??梢员苊怙@式的遍歷。

例如下面這段代碼:

 

  1. List<String> list = new ArrayList<String>(); 
  2.  
  3. list.add("1"); 
  4. list.add("2"); 
  5.  
  6. for (String item : list) { 
  7.     if ("1".equals(item)) { 
  8.         list.remove(item); 
  9.         System.out.println("執(zhí)行if語句成功"); 
  10.     } 

直接運(yùn)行這段代碼是沒什么問題的,數(shù)組list能成功刪除元素1,也能打印對應(yīng)語句。

但是,我們進(jìn)行如下任意一種操作:

  • 若把list.remove(item)換成list.add(“3”);操作如何?
  • 若在第6行添加list.add("3");那么代碼會(huì)出錯(cuò)嗎?
  • 若把if語句中的“1”換成“2”,結(jié)果你感到意外嗎?

如果都能正確執(zhí)行當(dāng)然就不需要問了,所以3個(gè)都會(huì)報(bào)ConcurrentModificationException的異常;

 

 

執(zhí)行結(jié)果異常

 

 

而出現(xiàn)這些情況的原因稍稍解釋下就是:

首先,這涉及多線程操作,Iterator是不支持多線程操作的,List類會(huì)在內(nèi)部維護(hù)一個(gè)modCount的變量,用來記錄修改次數(shù)。

舉例:ArrayList源碼

 

  1. protected transient int modCount = 0; 

每生成一個(gè)Iterator,Iterator就會(huì)記錄該modCount,每次調(diào)用next()方法就會(huì)將該記錄與外部類List的modCount進(jìn)行對比,發(fā)現(xiàn)不相等就會(huì)拋出多線程編輯異常。

為什么這么做呢?我的理解是你創(chuàng)建了一個(gè)迭代器,該迭代器和要遍歷的集合的內(nèi)容是緊耦合的,意思就是這個(gè)迭代器對應(yīng)的集合內(nèi)容就是當(dāng)前的內(nèi)容,我肯定不會(huì)希望在我冒泡排序的時(shí)候,還有線程在向我的集合里插入數(shù)據(jù)對吧?所以Java用了這種簡單的處理機(jī)制來禁止遍歷時(shí)修改集合。

至于為什么刪除“1”就可以呢,原因在于foreach和迭代器的hasNext()方法,foreach這個(gè)語法,實(shí)際上就是

 

  1. while(itr.hasNext()){ 
  2.     itr.next() 

所以每次循環(huán)都會(huì)先執(zhí)行hasNext(),那么看看ArrayList的hasNext()是怎么寫的:

 

  1. public boolean hasNext() { 
  2.     return cursor != size

cursor是用于標(biāo)記迭代器位置的變量,該變量由0開始,每次調(diào)用next執(zhí)行+1操作,于是:

所以代碼在執(zhí)行刪除“1”后,size=1,cursor=1,此時(shí)hasNext()返回false,結(jié)束循環(huán),因此你的迭代器并沒有調(diào)用next查找第二個(gè)元素,也就無從檢測modCount了,因此也不會(huì)出現(xiàn)多線程修改異常;但當(dāng)你刪除“2”時(shí),迭代器調(diào)用了兩次next,此時(shí)size=1,cursor=2,hasNext()返回true,于是迭代器傻乎乎的就又去調(diào)用了一次next(),因此也引發(fā)了modCount不相等,拋出多線程修改的異常。

當(dāng)你的集合有三個(gè)元素的時(shí)候,你就會(huì)神奇的發(fā)現(xiàn),刪除“1”是會(huì)拋出異常的,但刪除“2”就沒有問題了,究其原因,和上面的程序執(zhí)行順序是一致的。

因此,在《阿里巴巴Java開發(fā)手冊中有這樣一條規(guī)定》:

 

 

 

 

3.2、轉(zhuǎn)換

如果需要遍歷列表或數(shù)組,并取代它的部分或者全部元素值,就需要使用列表迭代器或者數(shù)組索引,以便替換元素的值。

因?yàn)閒or-each是一循到底的,中間不做停留和位置信息的顯示;所以要替換元素就不能使用它了。

3.3、平行迭代

如果需要并行的遍歷多個(gè)集合,就需要顯式的控制迭代器或者索引變量,以便所有迭代器或者索引變量都可以同步前進(jìn)(就像上面講述Iterator迭代器的時(shí)候提到的組合減少的情況,只想出現(xiàn)下標(biāo)一一對應(yīng)的元素組合)。

四、總結(jié)

for-each循環(huán)不僅適用于遍歷集合和數(shù)組,而且能讓你遍歷任何實(shí)現(xiàn)Iterator接口的對象;最最關(guān)鍵的是它還沒有性能損失。而對數(shù)組或集合進(jìn)行修改(添加刪除操作),就要用for循環(huán)。

所以循環(huán)遍歷所有數(shù)據(jù)的時(shí)候,能用它的時(shí)候還是選擇它吧,嘻嘻。

 

責(zé)任編輯:華軒 來源: Java極客技術(shù)
相關(guān)推薦

2022-08-11 17:14:37

Java

2022-12-09 09:46:55

插件Lombok

2024-08-22 08:17:55

C#工具循環(huán)

2020-03-31 10:58:38

2019-11-27 10:54:43

Tomcat連接數(shù)線程池

2024-01-08 08:27:11

注解Bean代理

2023-06-08 11:57:15

Matter協(xié)議家庭智能

2017-11-07 12:35:53

比特幣區(qū)塊鏈虛擬貨幣

2018-03-27 08:46:01

數(shù)據(jù)庫NoSQLredis

2020-11-20 07:58:04

Java

2021-12-22 14:20:31

語言人工智能機(jī)器學(xué)習(xí)

2017-12-07 15:00:00

筆記本OLED屏幕

2021-11-08 10:00:19

require前端模塊

2021-07-21 10:10:14

require前端代碼

2018-06-29 08:36:50

2019-09-02 08:39:02

路由器RAM內(nèi)存

2019-08-27 08:24:17

簡歷技能工作

2022-06-27 07:32:00

JavaArrayList語法糖

2014-04-17 16:42:03

DevOps

2022-07-26 00:00:22

HTAP系統(tǒng)數(shù)據(jù)庫
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)