Java中使用for循環刪除集合中元素需要注意的點


刪除集合中的元素,第一反應是遍歷集合,比較找到相應的元素然后刪除。遍歷集合最容易想到的是for循環。

刪除集合中為3的元素:

 1 List<Integer> list = new ArrayList<Integer>();
 2         for(int i = 0;i<10;i++){
 3             list.add(i);
 4         }
 5         for(int i = 0;i<10;i++){
 6             list.add(i);
 7         }
 8         System.out.println("刪除前"+list);
 9         System.out.println("size:"+list.size());
10         for(int i = 0;i<list.size();i++){
11             if(list.get(i)==3){
12                 list.remove(i);
13             }
14         }
15         System.out.println("刪除后"+list);
16         System.out.println("size:"+list.size());

運行結果:

刪除前[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
size:20
刪除后[0, 1, 2, 4, 5, 6, 7, 8, 9, 0, 1, 2, 4, 5, 6, 7, 8, 9]
size:18

 看起來一點問題也沒有,那么我們把集合中元素的順序換一下。

 1 List<Integer> list = new ArrayList<Integer>();
 2         for(int i = 0;i<10;i++){
 3             list.add(i);
 4             list.add(i);
 5         }
 6         System.out.println("刪除前"+list);
 7         System.out.println("size:"+list.size());
 8         for(int i = 0;i<list.size();i++){
 9             if(list.get(i)==3){
10                 list.remove(i);
11             }
12         }
13         System.out.println("刪除后"+list);
14         System.out.println("size:"+list.size());

運行結果:

刪除前[0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9]
size:20
刪除后[0, 0, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9]
size:19

問題出現了,當刪除相鄰兩個重復元素的時候,只刪除一個,這是什么原因呢?

ArrayList的底層結構是數組類型,數組的特點是刪除某個元素時,后面所有元素的索引都會往前移,而此時for循環的指針是卻是向下移動的。

 

解決方案一:使用每次刪除元素的時候,將for循環指針回調一次

 1 List<Integer> list = new ArrayList<Integer>();
 2         for(int i = 0;i<10;i++){
 3             list.add(i);
 4             list.add(i);
 5         }
 6         System.out.println("刪除前"+list);
 7         System.out.println("size:"+list.size());
 8         for(int i = 0;i<list.size();i++){
 9             if(list.get(i)==3){
10                 list.remove(i);
11                 i--;
12             }
13         }
14         System.out.println("刪除后"+list);
15         System.out.println("size:"+list.size());

運行結果:

刪除前[0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9]
size:20
刪除后[0, 0, 1, 1, 2, 2, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9]
size:18

解決方案二:使用迭代器

 1     List<Integer> list = new ArrayList<Integer>();
 2         for(int i = 0;i<10;i++){
 3             list.add(i);
 4             list.add(i);
 5         }
 6         System.out.println("刪除前"+list);
 7         System.out.println("size:"+list.size());
 8         Iterator<Integer> it = list.iterator();
 9         while(it.hasNext()){
10             if(3==it.next()){
11                 it.remove();
12             }
13         }
14         System.out.println("刪除后"+list);
15         System.out.println("size:"+list.size());

運行結果:

刪除前[0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9]
size:20
刪除后[0, 0, 1, 1, 2, 2, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9]
size:18

 為什么使用迭代器可以實現這樣的操作呢?下面將基於ArrayList的Iterator的實現分析Iterator的原理。

在ArrayList類中有個方法iterator(),此方法將返回一個iterator的實現,這里可以看出實現類叫Itr,通過其它源碼可知,此類是AarryList的內部類,即ArryList的Iterator實現在ArrayList內部

1  public Iterator<E> iterator() {
2         return new Itr();
3     }

ArrayList中實現類Itr類的源碼:

 1 private class Itr implements Iterator<E> {
 2         /**
 3          * 下一個返回的位置
 4          */
 5         int cursor = 0;
 6 
 7         /**
 8          * 當前操作的位置
 9          */
10         int lastRet = -1;
11 
12         /**
13          * 類似版本號,檢查List是否有更新
14          */
15         int expectedModCount = modCount;
16 
17         public boolean hasNext() {      // 判斷是否有下一個元素
18             return cursor != size();
19         }
20 
21         public E next() {  // 返回下一個元素
22             checkForComodification();
23             try {
24                 int i = cursor;     // cursor記錄的是下一個元素,所以調用next時將返回的是cursor對應的元素
25                 E next = get(i);    //  記錄需要返回的元素
26                 lastRet = i;        // 記錄當前元素
27                 cursor = i + 1;     // 記錄下一個元素
28                 return next;
29             } catch (IndexOutOfBoundsException e) {
30                 checkForComodification();
31                 throw new NoSuchElementException();
32             }
33         }
34 
35         public void remove() {       // 移除元素
36             if (lastRet < 0)
37                 throw new IllegalStateException();
38             checkForComodification();     //   檢查是否有更改,remove或者add
39 
40             try {
41                 AbstractList.this.remove(lastRet);   // 刪除當前元素  
42                 if (lastRet < cursor)                // 刪除了之后指標減1
43                     cursor--;
44                 lastRet = -1;                    
45                 expectedModCount = modCount;         // 保持版本號一致
46             } catch (IndexOutOfBoundsException e) {
47                 throw new ConcurrentModificationException();
48             }
49         }
50 
51         final void checkForComodification() {    // 如果有更改則拋出ConcurrentModificationException異常
52             if (modCount != expectedModCount)
53                 throw new ConcurrentModificationException();
54         }
55     }

在next()方法中可以看到,通過移動cursor游標來指向集合下一個元素。

再看remove()方法,代碼的43行,刪除當前元素后,游標前移了。

結論:這兩種方法,基本思想都是將指針回調。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM