您好,登錄后才能下訂單哦!
本篇內容主要講解“怎么移除List中的元素”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“怎么移除List中的元素”吧!
我們先看下這段代碼,你有沒有寫過類似的代碼
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
System.out.println("開始添加元素 size:" + list.size());
for (int i = 0; i < 100; i++) {
list.add(i + 1);
}
System.out.println("元素添加結束 size:" + list.size());
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
Integer next = iterator.next();
if (next % 5 == 0) {
list.remove(next);
}
}
System.out.println("執行結束 size:" + list.size());
}
「毫無疑問,執行這段代碼之后,必然報錯,我們看下報錯信息。」
我們可以通過錯誤信息可以看到,具體的錯誤是在checkForComodification
這個方法產生的。
首先我們看下ArrayList
的iterator
這個方法,通過源碼可以發現,其實這個返回的是ArrayList
內部類的一個實例對象。
public Iterator<E> iterator() {
return new Itr();
}
我們看下Itr
類的全部實現。
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() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
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 {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
「參數說明:」
cursor
: 下一次訪問的索引;
lastRet
:上一次訪問的索引;
expectedModCount
:對ArrayList修改次數的期望值,初始值為modCount
;
modCount
:它是AbstractList
的一個成員變量,表示ArrayList
的修改次數,通過add
和remove
方法可以看出;
「幾個常用方法:」
hasNext()
:
public boolean hasNext() {
return cursor != size;
}
如果下一個訪問元素的下標不等于size
,那么就表示還有元素可以訪問,如果下一個訪問的元素下標等于size
,那么表示后面已經沒有可供訪問的元素。因為最后一個元素的下標是size()-1
,所以當訪問下標等于size
的時候必定沒有元素可供訪問。
next()
:
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];
}
注意下,這里面有兩個非常重要的地方,cursor
初始值是0,獲取到元素之后,cursor
加1,那么它就是下次索要訪問的下標,最后一行,將i
賦值給了lastRet
這個其實就是上次訪問的下標。
此時,cursor
變為了1,lastRet
變為了0。
最后我們看下ArrayList
的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;
}
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
初始值是0,刪除一個元素之后,modCount
自增1,接下來就是刪除元素,最后一行將引用置為null
是為了方便垃圾回收器進行回收。
到這里,其實一個完整的判斷、獲取、刪除已經走完了,此時我們回憶下各個變量的值:
cursor
: 1(獲取了一次元素,默認值0自增了1);
lastRet
:0(上一個訪問元素的下標值);
expectedModCount
:0(初始默認值);
modCount
:1(進行了一次remove
操作,變成了1);
不知道你還記不記得,next()
方法中有兩次檢查,如果已經忘記的話,建議你往上翻一翻,我們來看下這個判斷:
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
當modCount
不等于expectedModCount
的時候拋出異常,那么現在我們可以通過上面各變量的值發現,兩個變量的值到底是多少,并且知道它們是怎么演變過來的。那么現在我們是不是清楚了ConcurrentModificationException
異常產生的愿意呢!
「就是因為,list.remove()
導致modCount
與expectedModCount
的值不一致從而引發的問題。」
我們現在知道引發這個問題,是因為兩個變量的值不一致所導致的,那么有沒有什么辦法可以解決這個問題呢!答案肯定是有的,通過源碼可以發現,Iterator
里面也提供了remove
方法。
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
你看它做了什么,它將modCount
的值賦值給了expectedModCount
,那么在調用next()
進行檢查判斷的時候勢必不會出現問題。
那么以后如果需要remove
的話,千萬不要使用list.remove()
了,而是使用iterator.remove()
,這樣其實就不會出現異常了。
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
System.out.println("開始添加元素 size:" + list.size());
for (int i = 0; i < 100; i++) {
list.add(i + 1);
}
System.out.println("元素添加結束 size:" + list.size());
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
Integer next = iterator.next();
if (next % 5 == 0) {
iterator.remove();
}
}
System.out.println("執行結束 size:" + list.size());
}
「建議:」
另外告訴大家,我們在進行測試的時候,如果找不到某個類的實現類,因為有時候一個類有超級多的實現類,但是你不知道它到底調用的是哪個,那么你就通過debug
的方式進行查找,是很便捷的方法。
到此,相信大家對“怎么移除List中的元素”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。