Java?ArrayList實(shí)現(xiàn)刪除指定位置的元素
目標(biāo):list中有0到39共40個(gè)元素,刪除其中索引是10、20、30的元素
方案一:使用普通for循環(huán)從前往后遍歷再刪除
//初始化List列表 List<String> list = new ArrayList<>(); for (int i = 0; i < 40; i++) { list.add("element" + i); }
首先當(dāng)我們刪除第10位元素時(shí),List會(huì)將后面的元素向前補(bǔ)位,之后再查第10位元素就會(huì)輸出第11位元素
for (int i = 0; i < list.size(); i++) { if (i == 10) { list.remove(i); } } System.out.println(list.get(10));
輸出:
element11
那么刪除了一個(gè)元素以后,后面需要?jiǎng)h除的元素位置就向前提1位
/** * 如果明確需要?jiǎng)h除元素的位置 * 那么可以這樣,每刪除一個(gè)元素后就把下一個(gè)要?jiǎng)h除元素的位置減1 * 注意這么做有個(gè)需要注意的點(diǎn),那就是每次刪除完節(jié)點(diǎn)后遍歷指針i需要減一,這樣在刪除兩個(gè)臨近節(jié)點(diǎn)時(shí)才不會(huì)出現(xiàn)問(wèn)題 * 比如要?jiǎng)h除10和11 */ for (int i = 0; i < list.size(); i++) { if (i == 10) { list.remove(i); i--; } if (i == 19) { list.remove(i); i--; } if (i == 28) { list.remove(i); i--; } } System.out.println(list.contains("element10")); System.out.println(list.contains("element20")); System.out.println(list.contains("element30"));
輸出:
false
false
false
當(dāng)然我們可以用一個(gè)數(shù)組或列表從小到大存儲(chǔ)需要?jiǎng)h除的位置,然后再for循環(huán)中進(jìn)行運(yùn)算和取值
方案二:使用普通for循環(huán)從后往前遍歷再刪除
從后向前遍歷的好處是我們不需要再像方案一一樣每刪除一個(gè)元素都需要去考慮后面元素向前補(bǔ)位的問(wèn)題
for (int i = list.size() - 1; i >= 0; i--) { if (i == 30) { list.remove(i); } if (i == 20) { list.remove(i); } if (i == 10) { list.remove(i); } } System.out.println(list.contains("element10")); System.out.println(list.contains("element20")); System.out.println(list.contains("element30"));
輸出:
false
false
false
從后向前,即使后面進(jìn)行元素進(jìn)行向前補(bǔ)位操作也不會(huì)影響前面需要?jiǎng)h除的元素
這里也可以用一個(gè)數(shù)組或列表存儲(chǔ)需要?jiǎng)h除的元素,從大到小排列,取出一個(gè)刪除一個(gè)
方案三:使用迭代器刪除
Iterator<String> iterator = list.iterator(); int i = 0; while (iterator.hasNext()) { String next = iterator.next(); if (i == 10) { iterator.remove(); } i++; } System.out.println(list.get(10)); System.out.println(list.contains("element10"));
輸出:
element11
false
在迭代器中維護(hù)一個(gè)數(shù)字i標(biāo)識(shí)遍歷的位置
如果我們?cè)诘髦欣^續(xù)刪除另外20和30位置元素
Iterator<String> iterator = list.iterator(); int i = 0; while (iterator.hasNext()) { String next = iterator.next(); if (i == 10) { iterator.remove(); } if (i == 20) { iterator.remove(); } if (i == 30) { iterator.remove(); } i++; } System.out.println(list.get(10)); System.out.println(list.contains("element10")); System.out.println(list.get(20)); System.out.println(list.contains("element20")); System.out.println(list.get(30)); System.out.println(list.contains("element30"));
輸出:
element11
false
element22
false
element33
false
首先我們?cè)诘^(guò)程中指定的是刪除10、20、30三個(gè)位置的元素,可以看到輸出contains時(shí)都是false表示正確刪除,但是最終輸出列表的值發(fā)現(xiàn)對(duì)應(yīng)索引位置已經(jīng)進(jìn)行了補(bǔ)位。
我們debug分析一下為什么
先簡(jiǎn)單介紹一下Iterator和Iterable
Iterable是一個(gè)迭代接口,實(shí)現(xiàn)了這個(gè)接口代表該類(lèi)可以迭代
可以看到我們的集合Collection接口就是它的子類(lèi)
它有一個(gè)主要方法:
// 返回一個(gè)實(shí)現(xiàn)了Iterator接口的對(duì)象,我們也是用這個(gè)對(duì)象去進(jìn)行迭代 Iterator<T> iterator();
Iterator,它主要有三個(gè)方法:
// 返回是否還有下一個(gè)元素 boolean hasNext(); // 返回下一個(gè)元素 E next(); // 刪除該元素 default void remove() { throw new UnsupportedOperationException("remove"); }
每個(gè)不同的集合類(lèi)都會(huì)有不同的Iterator接口實(shí)現(xiàn),在ArrayList中使用了一個(gè)內(nèi)部類(lèi)來(lái)實(shí)現(xiàn)
private class Itr implements Iterator<E> { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; Itr() {} }
我們通過(guò)list.iterator()拿到的就是這個(gè)內(nèi)部類(lèi)的對(duì)象實(shí)例,這個(gè)類(lèi)中有兩個(gè)字段cursor和lastRet,這兩個(gè)字段就是我們能在迭代器中正確刪除對(duì)應(yīng)位置的元素的關(guān)鍵。
有關(guān)expectedModCount和modCount的問(wèn)題后面會(huì)補(bǔ)充,我們先不用關(guān)注
cursor初始化是0 lastRet初始化是-1
分析next和remove方法的源碼
/** * 可以先不關(guān)注這兩個(gè)Exception * 每次調(diào)用next() cursor都會(huì)+1 而lastRet就會(huì)變成之前cursor的值 * cursor初始化是0 * lastRet初始化是-1 * 調(diào)用一次以后 cursor是1 lastRet變成0 **/ public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { // 調(diào)用本身的remove方法 ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } }
關(guān)鍵是這個(gè)remove方法對(duì)cursor和lastRet的修改
假如正在刪除第10個(gè)元素
執(zhí)行remove方法前cursor應(yīng)該是11,lastRet是10
執(zhí)行了以后lastRet變成了-1,cursor變成了10
下次執(zhí)行next()方法返回的元素其實(shí)還是elementData[10]
也就是List補(bǔ)位后正確的下一個(gè)元素,cursor變成了11,lastRet是10
總結(jié):使用迭代器遍歷時(shí)ArrayList會(huì)用lastRet和cursor兩個(gè)變量來(lái)維護(hù)當(dāng)前遍歷的元素索引和下一次需要遍歷元素的索引,通過(guò)這兩個(gè)變量就可以實(shí)現(xiàn)迭代中正確的刪除某個(gè)位置的元素。
到此這篇關(guān)于Java ArrayList實(shí)現(xiàn)刪除指定位置的元素的文章就介紹到這了,更多相關(guān)Java ArrayList刪除指定位置元素內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring?Boot自定義Starter組件開(kāi)發(fā)實(shí)現(xiàn)配置過(guò)程
SpringBoot中的starter是一種非常重要的機(jī)制,能夠拋棄以前繁雜的配置,將其統(tǒng)一集成進(jìn)?starter,應(yīng)用者只需要在maven中引入starter依賴,這篇文章主要介紹了Spring?Boot自定義Starter組件開(kāi)發(fā)實(shí)現(xiàn),需要的朋友可以參考下2022-06-06JavaWeb實(shí)現(xiàn)文件上傳與下載的方法
這篇文章主要介紹了JavaWeb實(shí)現(xiàn)文件上傳與下載的方法的相關(guān)資料,需要的朋友可以參考下2016-01-01Java 隨機(jī)生成驗(yàn)證碼(支持大小寫(xiě)字母、數(shù)字、隨機(jī)字體)的實(shí)例
java隨機(jī)產(chǎn)生驗(yàn)證碼,可以隨機(jī)生成數(shù)字、大寫(xiě)字母、小寫(xiě)字母。還可以隨機(jī)生成文字字體、及大小。在圖片上面可能字體都不不同、大小不等2013-05-05Java實(shí)現(xiàn)登錄密碼強(qiáng)度校驗(yàn)的項(xiàng)目實(shí)踐
本文主要介紹了Java實(shí)現(xiàn)登錄密碼強(qiáng)度校驗(yàn)的項(xiàng)目實(shí)踐,包括使用正則表達(dá)式匹配校驗(yàn)和密碼強(qiáng)度校驗(yàn)工具類(lèi)這兩種方法,具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01JAVA遞歸與非遞歸實(shí)現(xiàn)斐波那契數(shù)列
這篇文章主要為大家詳細(xì)介紹了JAVA遞歸與非遞歸實(shí)現(xiàn)斐波那契數(shù)列,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02List轉(zhuǎn)換成Map工具類(lèi)的簡(jiǎn)單實(shí)例
下面小編就為大家?guī)?lái)一篇List轉(zhuǎn)換成Map工具類(lèi)的簡(jiǎn)單實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-01-01