新聞中心
我們都知道 List 中是不允許在循環(huán)的過(guò)程中去進(jìn)行移除元素的,為什么呢?一般的新人可能會(huì)遇到這個(gè)問(wèn)題,比如說(shuō)會(huì)從 List 的遍歷的過(guò)程中去進(jìn)行 remove 數(shù)據(jù),但是干過(guò)幾年的開(kāi)發(fā)的有經(jīng)驗(yàn)的工作人員,是肯定不會(huì)這么干的,很簡(jiǎn)單,會(huì)報(bào)錯(cuò)。

List 進(jìn)行 remove
我們可以來(lái)看一段代碼:
public static void main(String[] args) {
List list= new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
for (String s: list) {
if (s.equals("1")) {
list.remove(s);
}
}
System.out.println(list);
} 上面這段代碼,一般都是初入開(kāi)發(fā)行業(yè)的小伙伴可能會(huì)這么寫,但是當(dāng)你去運(yùn)行的時(shí)候,就會(huì)發(fā)現(xiàn)會(huì)報(bào)一個(gè)錯(cuò)誤。
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
at com.example.fastdfs.Test.main(Test.java:22)
但是當(dāng)我們把代碼改成刪除元素 2 的時(shí)候,發(fā)現(xiàn)又成功了?。?!
是真的,成功了,我們看代碼和運(yùn)行結(jié)果:
public static void main(String[] args) {
List list= new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
for (String s: list) {
if (s.equals("2")) {
list.remove(s);
}
}
System.out.println(list);
} 運(yùn)行結(jié)果如下:
為什么會(huì)出現(xiàn)這種情況,我刪除第一個(gè)元素不行,刪除第二個(gè)元素好用,刪除第三個(gè)元素又不行了,到底是什么原因?qū)е碌哪兀?/p>
這時(shí)候我們就得去看看他的源碼編譯出來(lái)是什么樣子的。源碼如下:
public static void main(String[] args) {
List list = new ArrayList();
list.add("1");
list.add("2");
list.add("3");
Iterator var2 = list.iterator();
while(var2.hasNext()) {
String s = (String)var2.next();
if (s.equals("3")) {
list.remove(s);
}
}
System.out.println(list);
} 也就是說(shuō),foreach 的循環(huán)內(nèi)部,就是采用的iteratior形式,使用的核心方法是hasnext()和next()。
既然都使用迭代器了,為啥還是不行呢?我們來(lái)看看迭代器的源碼,然后分析一下為啥不行.
其實(shí)我們可以從報(bào)錯(cuò)都能看出點(diǎn)端倪,報(bào)錯(cuò)信息是ArrayList.java:909
checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}源代碼在執(zhí)行 remove 方法的時(shí)候后,也是調(diào)用的 list 當(dāng)中的remove 方法,源代碼中,就是這段:
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}中間調(diào)用的 fastRemove 方法中,中間就看到了:
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}這里我們的modCount++了
而當(dāng)我們?cè)僖淮窝h(huán)的時(shí)候,調(diào)用的是list內(nèi)部類itr的next方法,
在我們調(diào)用的list的remove的時(shí)候,modCount++了,而我們的expectedModCount是等于最開(kāi)始modCount值.
這時(shí)候二者的值不相等的時(shí)候,就出現(xiàn)異常了。
歸根結(jié)底,雖然這個(gè)地方使用的是迭代器的遍歷,但是remove 的方法可不是迭代器的方法呀。
那么我們使用迭代器遍歷然后移除是什么樣子的呢?
public static void main(String[] args) {
List list= new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
Iterator iterator = list.iterator();
while (iterator.hasNext()){
String next = iterator.next();
if ("3".equals(next)){
iterator.remove();
}
}
System.out.println(list);
} 這么寫是不是有點(diǎn)多,那么應(yīng)該如何快速的寫完這段代碼呢?
其實(shí)一行代碼就能很快解決這個(gè)事情,我們先來(lái)看代碼怎么寫的:
list.removeIf(vo-> "3".equals(vo));
也不用管返回值了,只要是能滿足這個(gè)條件的 ,那么就會(huì)從集合中給移除掉。
話不多說(shuō),看結(jié)果:
這么一看,是不是發(fā)現(xiàn)非常簡(jiǎn)單方便,而且還快捷,而且如果要是對(duì)于代碼量來(lái)說(shuō)的話,那肯定是非常的少的,但凡滿足條件的,肯定可以。
removeIf 的進(jìn)階玩法
阿粉為什么稱之為進(jìn)階玩法,實(shí)際上也并不是完整的進(jìn)階玩法,比如說(shuō)如果我們有一個(gè)功能是這樣的,要求做一個(gè)導(dǎo)入的功能,然后導(dǎo)入的數(shù)據(jù)只有一個(gè)車牌號(hào)是唯一值,之前導(dǎo)入的數(shù)據(jù),不做處理,新增的文件中,可能會(huì)包含所有的數(shù)據(jù),要求數(shù)據(jù)庫(kù)中已經(jīng)存在的數(shù)據(jù),不處理,然后導(dǎo)入數(shù)據(jù)庫(kù)中不存在的數(shù)據(jù)。
如果字段少的話,那么實(shí)現(xiàn)思路可能會(huì)有幾種。
第一種:
mybatis 的 SelectKey 標(biāo)簽,判斷是否存在,如果存在就不進(jìn)行新增。
第二種:
導(dǎo)入之前,查詢數(shù)據(jù)庫(kù)數(shù)據(jù),比對(duì)數(shù)據(jù),然后直接進(jìn)行remove,最后不存在的數(shù)據(jù)導(dǎo)入
這兩種方法實(shí)際上都能實(shí)現(xiàn),但是他們的適用情況就不太一樣了,如果字段非常多呢?
自己寫sql 的話,那么代價(jià)實(shí)在是有點(diǎn)大,如果你們使用的還是 Mybatis-plus 的話,那么肯定第一種方式好像就沒(méi)辦法使用了,只能使用第二種了。
那么我們的 removeIf 應(yīng)該怎么來(lái)寫呢?
//創(chuàng)建第一個(gè)UserList
ListuserList = new ArrayList<>();
User user = new User();
user.setId(UUID.randomUUID().toString());
user.setName("張三");
user.setAge(20);
user.setDept("開(kāi)發(fā)部");
userList.add(user);
User user1 = new User();
user1.setId(UUID.randomUUID().toString());
user1.setName("李四");
user1.setAge(22);
user1.setDept("測(cè)試部");
userList.add(user1);
User user2 = new User();
user2.setId(UUID.randomUUID().toString());
user2.setName("王五");
user2.setAge(27);
user2.setDept("財(cái)務(wù)部");
userList.add(user2);
//創(chuàng)建第二個(gè)UserList
ListuserEnd= new ArrayList<>();
User user3= new User();
user3.setId(UUID.randomUUID().toString());
user3.setName("張三");
user3.setAge(20);
user3.setDept("開(kāi)發(fā)部");
userEnd.add(user3);
User user4= new User();
user4.setId(UUID.randomUUID().toString());
user4.setName("李四");
user4.setAge(22);
user4.setDept("測(cè)試部");
userEnd.add(user4);
如果我們這時(shí)候要把第一個(gè)userList 中的數(shù)據(jù)導(dǎo)入數(shù)據(jù)庫(kù),而 userEnd 則是數(shù)據(jù)庫(kù)中的數(shù)據(jù),這時(shí)候,我們要根據(jù)姓名來(lái)區(qū)分的話,是不是應(yīng)該之導(dǎo)入王五才對(duì),這時(shí)候我們得篩選出王五的數(shù)據(jù)來(lái),然后做導(dǎo)入,這個(gè)時(shí)候 removeIf 就派上用場(chǎng)了。
userList.removeIf(us1-> userEnd.stream().anyMatch(u ->us1.getName().equals(u.getName())));
System.out.println(Arrays.toString(userList.toArray()));
我們最后來(lái)看看結(jié)果:
[User(id=029b0b0f-ad42-4c15-8341-a3bb401be6d6, name=王五, age=27, dept=財(cái)務(wù)部)]
是不是已經(jīng)做到了呢?
你學(xué)會(huì)了么?
網(wǎng)站題目:還在自己寫迭代器進(jìn)行remove?快來(lái)看看新方法
文章起源:http://m.fisionsoft.com.cn/article/ccsesge.html


咨詢
建站咨詢
