list集合刪除不要使用加強for,建議使用for(int i;;)這種方法,注意這種方法刪除集合元素會致使索引前移致使遍歷問題bash
例如:多線程
private static void delFor() {
List<String> blist = new ArrayList<>();
blist.add("a");
blist.add("b");
blist.add("c");
blist.add("d");
blist.add("e");
blist.add("f");
for (int i = 0; i < blist.size(); i++) {
if(blist.get(i).equals("b")) {
blist.remove(blist.get(i));
//這裏輸出被刪除的元素有問題,主要是索引改變,刪除自己沒問題
System.out.println("刪除的元素是: " + blist.get(i));
}
}
System.out.println(blist);
}
複製代碼
加強for遍歷等效於使用迭代器, 在遍歷中修改元素數目會報ConcurrentModificationExceptionide
private static void delNormal() {
List<String> alist = new ArrayList<>();
alist.add("1");
alist.add("2");
alist.add("3");
for (String item : alist) {
if (item.equals("2")) { //刪除2不報錯
alist.remove(item);
System.out.println("被刪除的元素"+item);
}
}
System.out.println("遍歷刪除後的集合" + " " + alist);
}
結果:被刪除的元素2
遍歷刪除後的集合 [1, 3]
複製代碼
上面結論你們確定知道,可是看這段代碼,刪除成功了,並無報錯,結論不對嗎?ui
再看一段代碼:this
private static void del() {
List<String> blist = new ArrayList<>();
blist.add("a");
blist.add("b");
blist.add("c");
blist.add("d");
blist.add("e");
blist.add("f");
for (String item : blist) {
if(item.equals("b")) {
blist.remove(item);
System.out.println("刪除的元素是: "+ item );
}
}
System.out.println("刪除後的集合爲:" +blist);
}
複製代碼
這段代碼運行直接報錯,是上面描述的異常spa
private static void delForIterator() {
List<String> blist = new ArrayList<>();
blist.add("a");
blist.add("b");
blist.add("c");
blist.add("d");
blist.add("e");
blist.add("f");
Iterator it = blist.iterator();
while(it.hasNext()) {
String item = (String) it.next();
if(item.equals("b")) {
it.remove();
System.out.println("刪除的元素是: "+ item );
}
}
System.out.println("刪除後的集合爲:" +blist);
}
複製代碼
正確刪除,沒問題線程
咱們看下迭代刪除的源碼:code
private class Itr implements Iterator<E> {
int cursor; // /將要訪問的元素的索引
int lastRet = -1; // 上一個訪問元素的索引
int expectedModCount = modCount;//expectedModCount爲預期修改值,初始化等於modCount(AbstractList類中的一個成員變量)
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
//每次調用next()須要check
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值相等
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
複製代碼
在獲取一個Iterator對象時,會初始化成員變量orm
對於第一段代碼(加強for底層仍是調用迭代器),不報錯是由於在刪除2之後,調用hasNext()方法,cursor值移動至2,size此時變成2,相等,跳出循環,因此沒有報錯,這僅僅是個巧合而已。對象
增長for刪除報錯的主要緣由是每次調用next()方法,都會檢查expectedModCount和 ModCount值是否相等,當咱們刪除元素後,ModCount會改變與expectedModCount值不一樣,引發報錯。
使用iterator刪除時,看上面源碼,會再次賦值它們相等,因此不會報錯。
使用迭代器的iterator.remove()在單線程下是不會報錯的,可是在多線程狀況下,一個線程修改了集合的modCount致使另一個線程迭代時modCount與該迭代器的expectedModCount不相等,這也會報異常。
public class RemoveListForThreads implements Runnable {
static List<String> alist = new ArrayList<>();
public static void main(String[] args) {
RemoveListForThreads s = new RemoveListForThreads();
alist.add("a");
alist.add("b");
alist.add("c");
alist.add("d");
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(2);
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 1,
TimeUnit.SECONDS, workQueue);
for (int i = 0; i < 5; i++) {
executor.execute(s);
}
executor.shutdown();
}
@Override
public synchronized void run() {
Iterator<String> iterator = alist.iterator();
while (iterator.hasNext()) {
String s = iterator.next();
if (s.equals("c") && Thread.currentThread().getName().equals("pool-1-thread-1")) {
iterator.remove();
System.out.println(Thread.currentThread().getName() + " " + s);
}
System.out.println(Thread.currentThread().getName() + " " + s);
}
System.out.println(alist);
}
}
複製代碼
上面這段代碼建立了一個線程池,開啓了五個線程,在run方法中遍歷並刪除「b」元素,若是該方法不加同步鎖sychronized,也會拋出異常