List 中正確的增刪操做

這個爲何要單獨說的緣由是,在開發中的對數據庫中的增刪爲最基本的,可是是否是寫對了就尤其重要java

先來看代碼:數據庫

 1 public void testLoopInList(){
 2         List<String> a = new ArrayList<String>();
 3         a.add("1");
 4         a.add("2");
 5         a.add("w");
 6         for (String string : a) {
 7             System.out.println(string);
 8         }
 9         
10         for (String temp : a) {
11             if("2".equals(temp)){
12                 a.remove(temp);
13             }
14         }
15         
16         for (String string : a) {
17             System.out.println(string);
18         }

輸出:oop

1
2

java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
    at java.util.ArrayList$Itr.next(ArrayList.java:831)
    at test.Test_ForeachAndIterator.testLoopInList(Test_ForeachAndIterator.java:19)

出現錯誤了this

緣由:首先從錯誤中能夠看出,是ArraList中的Iterator的checkForComodification()出現的錯誤,說明了foreach的實現原理其實就是實現了內部類I特Iterator來進行遍歷的,其次爲何會出錯呢?spa

由於在ArrayList和ArrayList的內部類的Iterator中的都會存在remove的方法,而ArrayList和Iterator中都各自有本身的維持長度的變量,前者是modCount,後者是expectModCount,可是源碼中ArrayList的remove方法是會改變modCount的值,但卻不會直接同步到expectModCount的值的,而Iterator中時刻都插入了checkForComodification()方法來監測modCount是否與expectModCount相等,因此就會很容易出現異常錯誤,固然下面的代碼也是錯的code

 1     public void testLoopInList(){
 2         List<String> a = new ArrayList<String>();
 3         a.add("1");
 4         a.add("2");
 5         
 6         for (String string : a) {
 7             System.out.println(string);
 8         }
 9         
10         Iterator<String> it = a.iterator();
11         while(it.hasNext()){
12             if("1".equals(it.next())){
13                 a.remove(it.next());
14             }
15         }
16         
17         for (String string : a) {
18             System.out.println(string);
19         }
20     }

錯誤緣由同上blog

因此在解決問題的關鍵就是要避免這個異常的出現,也就是時刻讓modCount==expectModCount,因此就是使用iterator的remove方法,由於會有使二者相等的代碼索引

即下面:ip

 1 public void testLoopInList(){
 2         List<String> a = new ArrayList<String>();
 3         a.add("1");
 4         a.add("2");
 5         
 6         for (String string : a) {
 7             System.out.println(string);
 8         }
 9         
10         Iterator<String> it = a.iterator();
11         while(it.hasNext()){
12             if("1".equals(it.next())){
13                 it.remove();
14             }
15         }
16         
17         for (String string : a) {
18             System.out.println(string);
19         }
20     }

 

相關源碼提上:element

 1 private class Itr implements Iterator<E> {
 2    /**
 3     * Index of element to be returned by subsequent call to next.
 4     */
 5    int cursor = 0;
 6    /**
 7     * Index of element returned by most recent call to next or
 8     * previous.  Reset to -1 if this element is deleted by a call
 9     * to remove.
10     */
11    int lastRet = -1;
12    /**
13     * The modCount value that the iterator believes that the backing
14     * List should have.  If this expectation is violated, the iterator
15     * has detected concurrent modification.
16     */
17    int expectedModCount = modCount;
18    public boolean hasNext() {
19            return cursor != size();
20    }
21    public E next() {
22            checkForComodification();
23        try {
24        E next = get(cursor);
25        lastRet = cursor++;
26        return next;
27        } catch (IndexOutOfBoundsException e) {
28        checkForComodification();
29        throw new NoSuchElementException();
30        }
31    }
32    public void remove() {
33        if (lastRet == -1)
34        throw new IllegalStateException();
35            checkForComodification();
36        try {
37        AbstractList.this.remove(lastRet);
38        if (lastRet < cursor)
39            cursor--;
40        lastRet = -1;
41        expectedModCount = modCount;
42        } catch (IndexOutOfBoundsException e) {
43        throw new ConcurrentModificationException();
44        }
45    }
46    final void checkForComodification() {
47        if (modCount != expectedModCount)
48        throw new ConcurrentModificationException();
49    }

ArrayList中的remove

 1     public boolean remove(Object o) {
 2         if (o == null) {
 3             for (int index = 0; index < size; index++)
 4                 if (elementData[index] == null) {
 5                     fastRemove(index);
 6                     return true;
 7                 }
 8         } else {
 9             for (int index = 0; index < size; index++)
10                 if (o.equals(elementData[index])) {
11                     fastRemove(index);
12                     return true;
13                 }
14         }
15         return false;
16     }

 

 

固然你說我不用for加強,就用普通的for ,就不會有建立Iterator的操做,進而就不會有Iterator的實時監測維護值得操做,也行啊,但仍是要注意有坑啊! 好比咱們來看下面的代碼:

 1 List list = new ArrayList();
 2         Collections.addAll(list, 1, 2, 3, 4, 5);
 3         for(int i = 0; i < list.size(); ++i){
 4             int val = (int) list.get(i);
 5             
 6             // 需求是刪除 3 和 4 兩個元素
 7             if(3 == val || 4 == val){
 8                 list.remove(i);
 9             }
10         }

 

這是咱們很是常見的寫法,結果 爲 :1 2 3 4

奇怪了,4爲甚沒有刪除掉,其實仔細想一想 仍是能相同的 刪除了3以後 i = 3 size = 4,這時候,list.get返回值爲5, 會跳過4

因此 修改的話,兩個 liter tips : 要麼本身維護索引,要麼反向循環

1         for(int i = list.size() - 1; i > 0; --i){
2             int val = (int) list.get(i);
3             
4             // 需求是刪除 3 和 4 兩個元素
5                         if(3 == val || 4 == val){
6                             list.remove(i);
7                         }
8         }

 

 

1 for(int i = 0; i < list.size(); ++i){
2             int val = (int) list.get(i);
3             
4             // 需求是刪除 3 和 4 兩個元素
5             if(3 == val || 4 == val){
6                 list.remove(i);
7                 i --; // 刪除後,索引也應該變化
8             }
9         }

 

ok, that's all ...

相關文章
相關標籤/搜索