1. 程式人生 > 程式設計 >Java中List遍歷刪除元素remove()的方法

Java中List遍歷刪除元素remove()的方法

今天碰見根據條件進行list遍歷remove的問題,第一時間就是簡單for迴圈remove,只知道這麼寫不行,不安全,可是為什麼呢?你想過嗎?下面就關於List遍歷remove的問題,深挖一下!

一、幾種常見的遍歷方式

1、普通for迴圈

Java中List遍歷刪除元素remove()的方法

2、高階for迴圈

Java中List遍歷刪除元素remove()的方法

3、iterator和removeIf

Java中List遍歷刪除元素remove()的方法

4、stream()

Java中List遍歷刪除元素remove()的方法

5、複製

Java中List遍歷刪除元素remove()的方法

6、普通for迴圈 -->倒序方式

Java中List遍歷刪除元素remove()的方法

二、原始碼篇

1、普通for迴圈出錯原因

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 remove method that skips bounds checking and does not
 * return the value removed.
 */
private void fastRemove(int index) {
  modCount++;
  int numMoved = size - index - 1;
  if (numMoved > 0)
    //remove會導致之後的元素往前移動,而下標不改變時就會出現bug
    System.arraycopy(elementData,index+1,elementData,index,numMoved);
  elementData[--size] = null; // clear to let GC do its work
}

我們在刪除某個元素後,list的大小發生了變化,這時候你的的索引也會發生變化,這時就會導致你在遍歷的時候漏掉某些元素。
比如當你刪除第1個元素後,我們如果還是繼續根據索引訪問第2個元素時,因為刪除的關係,後面的元素都往前移動了一位,所以實際訪問的是第3個元素。
所以這種方式可以用在刪除特定的一個元素時使用,但不適合迴圈刪除多個元素時使用。

2、高階for迴圈出錯原因

foreach其實是用迭代器來進行遍歷的,而在遍歷時直接使用arraylist的remove方法會導致什麼問題呢?

可以再看一下fastremove和迭代器遍歷的內部程式碼:

Java中List遍歷刪除元素remove()的方法

Java中List遍歷刪除元素remove()的方法

最後導致丟擲上面異常的其實就是這個,簡單說,呼叫list.remove()方法導致modCount和expectedModCount的值不一致而報異常

final void checkForComodification() {
  if (modCount != expectedModCount)
    throw new ConcurrentModificationException();
}
//呼叫next時會呼叫checkForComodification方法檢查 這兩個欄位
//而fastRemove裡面只對modCount 進行了改變 導致丟擲異常
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];
}

所以遍歷時remove並不適用於foreach。

3、java8中新方法removeIf

//內部其實就是迭代器遍歷
default boolean removeIf(Predicate<? super E> filter) {
  Objects.requireNonNull(filter);
  boolean removed = false;
  final Iterator<E> each = iterator();
  while (each.hasNext()) {
    if (filter.test(each.next())) {
      each.remove();
      removed = true;
    }
  }
  return removed;
}

和迭代器差不多,內部實現也是迭代器。

三、總結

1、在不考慮記憶體大小會不會出現OOM的時候,採取複製一個新的list的方法速度更快,適用於集合中物件不算多的時候,畢竟只需要add操作。

2、當集合中元素過多時,複製list就顯得有些笨重了,採用迭代器的方式進行遍歷較快一些,並且不用關注小角標的變化。

3、不考慮效能的時候使用removeIf方法,程式碼簡潔明瞭。

4、當要針對角標進行元素的remove時,使用倒序遍歷的方式最為妥當。

到此這篇關於Java中List遍歷刪除元素remove()的方法的文章就介紹到這了,更多相關Java List遍歷刪除元素內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!