List 迴圈遍歷中刪除元素問題二
阿新 • • 發佈:2018-12-05
問題一可以看我之前寫的部落格List 迴圈遍歷中刪除元素問題一。
問題二主要講的是博主在今天的開發中遇到的一個問題,先來看下是什麼問題讓博主繼上次問題後又寫了問題二。
問題一中講到通過Iterator的remove方法解決連結串列中迴圈刪除元素的問題。來看下簡單的程式碼:
public class ListTest {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String s = iterator.next();
if ("4".equals(s)) {
iterator.remove();
}
}
System.out.println("list:" + list);
}
}
輸出:list:[1, 2, 3, 5],成功刪除元素4。
但是這種解決方法中隱藏著一個坑。在今天和前端的聯調過程中發現,我們一般從資料庫查詢多條資料時會返回List<?>,?表示具體的實體類,然後通過Iterator遍歷List刪除其中的元素,以下是類似的程式碼:
public class ListTest {
public static void main(String[] args) {
List<String> list = Arrays.asList("1","2","3","4","5");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String s = iterator.next();
if ("4".equals(s)) {
iterator.remove();
}
}
System.out.println("list:" + list);
}
}
原本以為通過iterator.remove()方法能夠成功刪除元素,但是上述程式碼在控制檯卻報了這麼一個錯:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.remove(AbstractList.java:161)
at java.util.AbstractList$Itr.remove(AbstractList.java:374)
at list.ListTest.main(ListTest.java:22)
一開始感到很困惑,但是在經過自己debug之後終於發現了問題所在,debug下進入的第一個方法:
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
在執行到AbstractList.this.remove(lastRet);
時進入下一個方法:
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
/*... 其它方法略...*/
public E remove(int index) {
throw new UnsupportedOperationException();
}
}
原來此處的ArrayList是一個實現了AbstractList的內部類,並且沒有覆蓋add和remove方法,預設這2個方法是會直接報“UnsupportedOperationException”的。
知道問題原因後,解決方法也就明確了。
public class ListTest {
public static void main(String[] args) {
List<String> list = Arrays.asList("1","2","3","4","5");
List<String> result= new ArrayList<>(list);
Iterator<String> iterator = result.iterator();
while (iterator.hasNext()) {
String s = iterator.next();
if ("4".equals(s)) {
iterator.remove();
}
}
System.out.println("list:" + result);
}
}
通過將list拷貝到一個新的ArrayList中即可解決問題二。
總結:在設計一個對外方法的時候,一點要謹慎處理集合和陣列。因為你永遠不知道傳給你的集合是什麼,也不知道是否會有對此集合有任何其他的不可控的操作。所以在使用客戶端傳遞的集合物件時,最好拷貝一個新集合後再操作。