背景
前一段時間被問到了關(guān)于 List 集合的安全刪除元素問題。一時間沒反應(yīng)過來這問題問的是什么,安全體現(xiàn)在什么地方,線程安全?線程安全可以保證元素粒度的數(shù)據(jù)唯一嗎?刪除是指什么,list.remove()?
帶著這些疑問,重溫了一下Java的集合知識。
問題分析
List為什么需要安全移除?
我不理解什么是安全刪除,我開發(fā)的業(yè)務(wù)中也很少說需要用到remove的,我只記得一般用的話,都是remove(index)這樣。寫個測試代碼看看
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
for (int i = 0; i < list.size(); i++) {
if ("B".equals(list.get(i))) {
list.remove(i);
continue;
}
System.out.println(list.get(i));
}
這段代碼的目的就是想把B移除,最后期望的輸出只有AC兩個字母,看一下運(yùn)行結(jié)果:
目的達(dá)到了,那這個刪除不就是安全刪除嗎?怎么才算安全刪除?
于是我又加了一堆 A B C D E F G,還是刪除B,最后打印的數(shù)組也是正確的。
直到我突發(fā)奇想,多加了一個連續(xù)重復(fù)的字母B,問題出現(xiàn)了
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("B");
list.add("C");
list.add("D");
for (int i = 0; i < list.size(); i++) {
if ("B".equals(list.get(i))) {
list.remove(i);
continue;
}
// System.out.println(list.get(i));
}
System.out.println(list);
我打印的結(jié)果是
[A, B, C, D]
為什么會這樣呢?其實(shí)原理很簡單,就是因?yàn)長ist.remove刪除元素后,數(shù)組的整體下標(biāo)會往前移動,原本的位置被遍歷過了,就會被跳過。
A | B | B | C | D | 遍歷index |
---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | 0遍歷元素A,不作任何操作 |
0 | 1 | 2 | 3 | 4 | 1遍歷第一個B,移除B |
0 | 1 | 2 | 3 | 2遍歷C,已經(jīng)跳過第二個B | |
0 | 1 | 2 | 3 | 3遍歷D,后面沒有元素了,結(jié)束 |
那簡單啊,我記得list移除操作可以remove(object),試一下
for (String element : list) {
if (element.equals("B")) {
// 在for循環(huán)中直接使用list.remove()方法刪除元素
list.remove(element);
}
}
System.out.println(list);
直接給我報錯了
分析了一下ArrayList的源碼,原來增強(qiáng)for循環(huán)的實(shí)現(xiàn)原理是使用了Iterator迭代器,而ArrayList重寫了迭代器的next方法,每次迭代時會檢查是否做了新增或者刪除操作(modCount++),而這些操作都會導(dǎo)致期待值與實(shí)際值不對等,從而拋出異常。
說簡單點(diǎn)就是和兩個B字母無關(guān),是你使用了增強(qiáng)for循環(huán)就不可以在遍歷的時候add和remove。
和上面相同的代碼原理是這樣的,使用迭代器遍歷list,隨后用ArrayList的remove
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
if (element.equals("B")) {
list.remove(element); // 直接使用list.remove()方法刪除元素
}
}
試了一下,也是報錯,一模一樣的問題。
到現(xiàn)在為止,我們理解了怎樣場景下移除元素是不安全的。不安全包括:
- 下標(biāo)上移導(dǎo)致的檢查丟失
- ConcurrentModificationException的發(fā)生
問題解決
方案一:
查閱了Java的API文檔之后,上面提到,使用Iterator自己的remove方法可以安全地移除元素。
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("B");
list.add("C");
list.add("D");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
if (element.equals("B")) {
iterator.remove(); // 安全移除元素
}
}
System.out.println(list);
輸出的結(jié)果為
[A, C, D]
方案二
Java8之后list新增了一個api removeIf,這個也可以做安全刪除
list.removeIf(s -> s.equals("B"));
輸出的結(jié)果為
[A, C, D]
方案三
使用removeAll方法文章來源:http://www.zghlxwxcb.cn/news/detail-492685.html
List<String> elementsToRemove = new ArrayList<>();
for (String element : list) {
if (element.equals("B")) {
elementsToRemove.add(element);
}
}
list.removeAll(elementsToRemove);
這樣執(zhí)行的結(jié)果也是正確的文章來源地址http://www.zghlxwxcb.cn/news/detail-492685.html
到了這里,關(guān)于Java list安全刪除元素詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!