Java?ArrayList實現(xiàn)刪除指定位置的元素
目標(biāo):list中有0到39共40個元素,刪除其中索引是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位元素時,List會將后面的元素向前補位,之后再查第10位元素就會輸出第11位元素
for (int i = 0; i < list.size(); i++) { if (i == 10) { list.remove(i); } } System.out.println(list.get(10));
輸出:
element11
那么刪除了一個元素以后,后面需要刪除的元素位置就向前提1位
/** * 如果明確需要刪除元素的位置 * 那么可以這樣,每刪除一個元素后就把下一個要刪除元素的位置減1 * 注意這么做有個需要注意的點,那就是每次刪除完節(jié)點后遍歷指針i需要減一,這樣在刪除兩個臨近節(jié)點時才不會出現(xiàn)問題 * 比如要刪除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)然我們可以用一個數(shù)組或列表從小到大存儲需要刪除的位置,然后再for循環(huán)中進(jìn)行運算和取值
方案二:使用普通for循環(huá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)行向前補位操作也不會影響前面需要刪除的元素
這里也可以用一個數(shù)組或列表存儲需要刪除的元素,從大到小排列,取出一個刪除一個
方案三:使用迭代器刪除
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ù)一個數(shù)字i標(biāo)識遍歷的位置
如果我們在迭代器中繼續(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
首先我們在迭代過程中指定的是刪除10、20、30三個位置的元素,可以看到輸出contains時都是false表示正確刪除,但是最終輸出列表的值發(fā)現(xiàn)對應(yīng)索引位置已經(jīng)進(jìn)行了補位。
我們debug分析一下為什么
先簡單介紹一下Iterator和Iterable
Iterable是一個迭代接口,實現(xiàn)了這個接口代表該類可以迭代
可以看到我們的集合Collection接口就是它的子類
它有一個主要方法:
// 返回一個實現(xiàn)了Iterator接口的對象,我們也是用這個對象去進(jìn)行迭代 Iterator<T> iterator();
Iterator,它主要有三個方法:
// 返回是否還有下一個元素 boolean hasNext(); // 返回下一個元素 E next(); // 刪除該元素 default void remove() { throw new UnsupportedOperationException("remove"); }
每個不同的集合類都會有不同的Iterator接口實現(xiàn),在ArrayList中使用了一個內(nèi)部類來實現(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() {} }
我們通過list.iterator()拿到的就是這個內(nèi)部類的對象實例,這個類中有兩個字段cursor和lastRet,這兩個字段就是我們能在迭代器中正確刪除對應(yīng)位置的元素的關(guān)鍵。
有關(guān)expectedModCount和modCount的問題后面會補充,我們先不用關(guān)注
cursor初始化是0 lastRet初始化是-1
分析next和remove方法的源碼
/** * 可以先不關(guān)注這兩個Exception * 每次調(diào)用next() cursor都會+1 而lastRet就會變成之前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)鍵是這個remove方法對cursor和lastRet的修改
假如正在刪除第10個元素
執(zhí)行remove方法前cursor應(yīng)該是11,lastRet是10
執(zhí)行了以后lastRet變成了-1,cursor變成了10
下次執(zhí)行next()方法返回的元素其實還是elementData[10]
也就是List補位后正確的下一個元素,cursor變成了11,lastRet是10
總結(jié):使用迭代器遍歷時ArrayList會用lastRet和cursor兩個變量來維護(hù)當(dāng)前遍歷的元素索引和下一次需要遍歷元素的索引,通過這兩個變量就可以實現(xiàn)迭代中正確的刪除某個位置的元素。
到此這篇關(guān)于Java ArrayList實現(xiàn)刪除指定位置的元素的文章就介紹到這了,更多相關(guān)Java ArrayList刪除指定位置元素內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring?Boot自定義Starter組件開發(fā)實現(xiàn)配置過程
SpringBoot中的starter是一種非常重要的機制,能夠拋棄以前繁雜的配置,將其統(tǒng)一集成進(jìn)?starter,應(yīng)用者只需要在maven中引入starter依賴,這篇文章主要介紹了Spring?Boot自定義Starter組件開發(fā)實現(xiàn),需要的朋友可以參考下2022-06-06Java 隨機生成驗證碼(支持大小寫字母、數(shù)字、隨機字體)的實例
java隨機產(chǎn)生驗證碼,可以隨機生成數(shù)字、大寫字母、小寫字母。還可以隨機生成文字字體、及大小。在圖片上面可能字體都不不同、大小不等2013-05-05