刪除集合中的元素,第一反應是遍歷集合,比較找到相應的元素然后刪除。遍歷集合最容易想到的是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行,刪除當前元素后,游標前移了。
結論:這兩種方法,基本思想都是將指針回調。