成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Java中簡單的For循環竟有這么多坑,你踩過嗎

開發 前端
關于JAVA中循環場景中對列表操作的相關內容我們就聊這么多了~ 你有踩過上面的坑么?你還有什么更好的方式來實現嗎?

大家好,又見面啦~

實際的業務項目開發中,大家應該對從給定的list中剔除不滿足條件的元素這個操作不陌生吧?

很多同學可以立刻想出很多種實現的方式,但你想到的這些實現方式都是人畜無害的嗎?很多看似正常的操作其實背后是個陷阱,很多新手可能稍不留神就會掉入其中。

倘若不幸踩中:

  • 代碼運行時直接拋異常報錯,這個算是不幸中的萬幸,至少可以及時發現并去解決。
  • 代碼運行不報錯,但是業務邏輯莫名其妙的出現各種奇怪問題,這種就比較悲劇了,因為這個問題稍不留神的話,可能就會給后續業務埋下隱患。

那么,到底有哪些實現方式呢?哪些實現方式可能會存在問題呢?這里我們一起探討下。注意哦,這里討論的可不是茴香豆的“茴”字有有種寫法的問題,而是很嚴肅很現實也很容易被忽略的技術問題。

假設需求場景:

給定一個用戶列表allUsers,需要從該列表中剔除隸屬部門為dev的人員,將剩余的人員信息返回。

踩坑操作?

foreach循環方式

很多新手的第一想法就是for循環逐個判斷校驗下然后符合條件的剔除掉就行了嘛~ so easy…

1分鐘就把代碼寫完了:

public List<UserDetail> filterAllDevDeptUsers(List<UserDetail> allUsers) {
for (UserDetail user : allUsers) {
// 判斷部門如果屬于dev,則直接剔除
if ("dev".equals(user.getDepartment())) {
allUsers.remove(user);
}
}
// 返回剩余的用戶數據
return allUsers;
}

然后信心滿滿的點擊了執行按鈕:

java.util.ConcurrentModificationException: null
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
at com.veezean.demo4.UserService.filterAllDevDeptUsers(UserService.java:13)
at com.veezean.demo4.Main.main(Main.java:26)

誒?what are you 弄啥嘞?咋拋異常了?

一不留神就踩坑里了,下面就一起分析下為啥會拋異常。

原因分析:

JAVA的foreach語法實際處理是基于迭代器Iterator進行實現的。

在循環開始時,會首先創建一個迭代實例,這個迭代實例的expectedModCount? 賦值為集合的modCount?。而每當迭代器使? hashNext()? / next()? 遍歷下?個元素之前,都會檢測 modCount? 變量與expectedModCount? 值是否相等,相等的話就返回遍歷;否則就拋出異常ConcurrentModificationException,終?遍歷。

如果在循環中添加或刪除元素,是直接調用集合的add(),remove()?方法,導致了modCount?增加或減少,但這些方法不會修改迭代實例中的expectedModCount?,導致在迭代實例中expectedModCount?與 modCount的值不相等,拋出ConcurrentModificationException異常。

圖片

下標循環操作

嗯哼?既然foreach方式不行,那就用原始的下標循環的方式來搞,總不會報錯了吧?依舊很easy …

public List<UserDetail> filterAllDevDeptUsers(List<UserDetail> allUsers) {
for (int i = 0; i < allUsers.size(); i++) {
// 判斷部門如果屬于dev,則直接剔除
if ("dev".equals(allUsers.get(i).getDepartment())) {
allUsers.remove(i);
}
}
// 返回剩余的用戶數據
return allUsers;
}

代碼一氣呵成,執行一下,看下處理后的輸出:

{id=2, name='李四', department='dev'}
{id=3, name='王五', department='product'}
{id=4, name='鐵柱', department='pm'}

果然,不報錯了,結果也輸出了,完美~

等等?這樣真的OK了嗎?

我們的代碼邏輯里面是判斷如果"dev".equals(department)?,但是輸出結果里面,為啥還是有department=dev這種本應被剔除掉的數據呢?

這里如果是在真實業務項目中,開發階段不報錯,又沒有仔細去驗證結果的情況下,流到生產線上,就可能造成業務邏輯的異常。

接下來看下出現這個現象的具體原因。

原因分析:

我們知道,list中的元素與下標之間,其實并沒有強綁定關系,僅僅只是一個位置順序的對應關系,list中元素變更之后,其每個元素對應的下標都可能會變更,如下示意:

圖片

那么,從List中刪除元素之后,List中被刪元素后面的所有元素下標都發生前移,但是for?循環的指針i是始終往后累加的,再處理下一個的時候,就可能會有部分元素被漏掉沒有處理。

比如下圖的示意,i=0?時,判斷A元素需要刪除,則直接刪除;再循環時i=1,此時因為list中元素位置前移,導致B元素變成了原來下標為0的位置,直接被漏掉了:

圖片

所以到這里呢,也就可以知道為啥上面的代碼執行后會出現漏網之魚啦~?

正確方式

見識了上面2個坑操作之后,那正確妥當的操作方式應該是怎么樣的呢?

迭代器方式

誒?沒搞錯吧?前面不是剛說過foreach方式也是使用的迭代器,但是其實是坑操作嗎?這里怎么又說迭代器模式是正確方式呢?

雖然都是基于迭代器,但是使用邏輯是不一樣的,看下代碼:

public List<UserDetail> filterAllDevDeptUsers(List<UserDetail> allUsers) {
Iterator<UserDetail> iterator = allUsers.iterator();
while (iterator.hasNext()) {
// 判斷部門如果屬于dev,則直接剔除
if ("dev".equals(iterator.next().getDepartment())) {
// 這是重點,此處操作的是Iterator,而不是list
iterator.remove();
}
}
// 返回剩余的用戶數據
return allUsers;
}

執行結果:

{id=3, name='王五', department='product'}
{id=4, name='鐵柱', department='pm'}

這次竟然直接執行成功了,且結果也是正確的。為啥呢?

在前面foreach?方式的時候,我們提過之所以會報錯的原因,是由于直接修改了原始list?數據而沒有同步讓Iterator?感知到,所以導致Iterator?操作前校驗失敗拋異常了。而此處的寫法中,直接調用迭代器中的remove()?方法,此操作會在調用集合的remove(),add()?方法后,將expectedModCount?重新賦值為modCount,所以在迭代器中增加、刪除元素是可以正常運行的。,所以這樣就不會出問題啦。

圖片

Lumbda表達式

言簡意賅,直接上代碼:

public List<UserDetail> filterAllDevDeptUsers(List<UserDetail> allUsers) {
allUsers.removeIf(user -> "dev".equals(user.getDepartment()));
return allUsers;
}

Stream流操作

作為JAVA8開始加入的Stream,使得這種場景實現起來更加的優雅與易懂:

public List<UserDetail> filterAllDevDeptUsers(List<UserDetail> allUsers) {
return allUsers.stream()
.filter(user -> !"dev".equals(user.getDepartment()))
.collect(Collectors.toList());
}

中間對象輔助方式

既然前面說了不能直接循環的時候執行移除操作,那就先搞個list對象將需要移除的元素暫存起來,最后一起剔除就行啦 ~

嗯,雖然有點挫,但是不得不承認,實際情況中,很多人都在用這個方法:

public List<UserDetail> filterAllDevDeptUsers(List<UserDetail> allUsers) {
List<UserDetail> needRemoveUsers = new ArrayList<>();
for (UserDetail user : allUsers) {
if ("dev".equals(user.getDepartment())) {
needRemoveUsers.add(user);
}
}
allUsers.removeAll(needRemoveUsers);
return allUsers;
}

或者:

public List<UserDetail> filterAllDevDeptUsers(List<UserDetail> allUsers) {
List<UserDetail> resultUsers = new ArrayList<>();
for (UserDetail user : allUsers) {
if (!"dev".equals(user.getDepartment())) {
resultUsers.add(user);
}
}
return resultUsers;
}

![](https://veezean-pics-1301558317.cos.ap-nanjing.myqcloud.com/pics/202207050811299.gif)

回顧

好啦,關于JAVA中循環場景中對列表操作的相關內容我們就聊這么多了~ 你有踩過上面的坑么?你還有什么更好的方式來實現嗎?

責任編輯:武曉燕 來源: 架構悟道
相關推薦

2020-06-01 08:04:18

三目運算符代碼

2022-09-27 10:52:25

Pythonprint函數

2023-11-13 08:49:54

2024-02-20 08:09:51

Java 8DateUtilsDate工具類

2024-04-01 08:05:27

Go開發Java

2020-04-07 19:16:31

微信隱藏功能移動應用

2018-12-05 14:29:22

2022-05-29 08:54:44

Edge瀏覽器

2017-07-12 08:20:32

閃存用途企業

2023-03-13 13:36:00

Go擴容切片

2018-06-26 15:00:24

Docker安全風險

2024-04-02 08:41:10

ArrayListSubList場景

2022-07-26 23:43:29

編程語言開發Java

2021-01-14 05:08:44

編譯鏈接

2017-12-21 19:38:50

潤乾中間表

2023-07-26 00:32:33

注解抽象spring

2018-09-11 09:14:52

面試公司缺點

2013-01-15 09:41:45

編程語言

2022-04-26 21:49:55

Spring事務數據庫

2017-07-04 14:01:40

機房機柜
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 91在线观看视频 | 黄片毛片免费观看 | 97国产在线观看 | 久久1区| 蜜桃久久 | 国产精品自产拍 | 国产男女视频网站 | 日韩不卡一二区 | 日韩中文字幕在线视频观看 | 国产精品视频专区 | 天天躁人人躁人人躁狂躁 | 国产91黄色 | 欧美精品在线免费观看 | 真人毛片| 久久国产传媒 | 久久久黑人 | 日一区二区 | 精品欧美一区二区三区精品久久 | 久久久婷| 免费大黄视频 | 日韩成人av在线 | 久久久久无码国产精品一区 | 久久久久久免费毛片精品 | 欧美中文在线 | 老妇激情毛片免费 | 日日做夜夜爽毛片麻豆 | 欧美一区二区三区一在线观看 | 精品久久久久久久久久 | 久久神马 | 一区二区三区视频在线观看 | 国产精品一区久久久 | 美女视频黄色片 | 日韩欧美不卡 | 一a级片 | www亚洲精品 | 色综合一区二区三区 | 日本三级电影免费观看 | 一级aaaaaa毛片免费同男同女 | 黄色高清视频 | av一区在线 | 精品一区二区三区中文字幕 |