原文鏈接:Iterator迭代器解決[爲何禁止在foreach內增刪]?
2020-01-13 12:08:06:332 星期一 🌙
迭代器的應用場景:
1、對集合進行增加刪除,禁止使用foreach,循環的動態操作
2、倒序遍歷
3、遍歷循環
步入正題:爲何禁止在foreach內進行增刪?
先看一下代碼:
/**
* 正例:
* Iterator<String> iterator = list.iterator();
* while (iterator.hasNext()) {
* String item = iterator.next();
* if (刪除元素的條件) {
* iterator.remove();
* }
* }
* 反例:
* List<String> list = new ArrayList<String>();
* list.add("1");
* list.add("2");
* for (String item : list) {
* if ("1".equals(item)) {
* list.remove(item);
* }
* }
*/
這段代碼是,在阿里的開發手冊中的一段代碼。
我們先看下面場景:
/**
* 場景一:對集合進行刪除,增加、for循環
* 錯誤:這裏會報出數據越界異常,
* 因爲:remove掉一個元素後,整個長度發生變化,所以發生異常
* 改進:採用forList.size()動態
*/
List<String> forList = new ArrayList<>();
forList.add("a");
forList.add("b");
forList.add("c");
int length = forList.size();
for (int i = 0; i < length; i++) {
if ("a".equals(forList.get(i))) {
forList.remove(i);
}
}
System.out.println(forList);
/**
* 產生新問題:
* 錯誤:運行便會發現:將b移除不完整,
* 因爲:刪除後整個遊標向下,數組向上,剛好空出1個位置,
* 緊接着的第二位沒有進行比對,所以產生問題
* 解決:數據長度減一與遊標保持統一
*/
List<String> forList1 = new ArrayList<>();
forList1.add("a");
forList1.add("b");
forList1.add("b");
forList1.add("c");
for (int i = 0; i < forList1.size(); i++) {
if ("b".equals(forList1.get(i))) {
forList1.remove(i);
i--;
}
}
System.out.println(forList1);
通過上個場景,知道在for循環內,爲啥不建議用remove/add
在foreach循環內,再接着看下面這個場景?
/**
* 場景二:
* foreach循環,的remove/add操作
*/
List<String> forEach = new ArrayList<>();
forEach.add("a");
forEach.add("b");
forEach.add("c");
for (String each : forEach) {
if ("a".equals(each)) {
forEach.remove(each);
}
}
/**
* 產生的異常:
* Exception in thread "main" java.util.ConcurrentModificationException
* at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
* at java.util.ArrayList$Itr.next(ArrayList.java:859)
*/
/**
* 源碼:Itr實現了Iterator接口(刪減部分)
* private class Itr implements Iterator<E> {
* @Override
* @SuppressWarnings("unchecked")
* public void forEachRemaining(Consumer<? super E> consumer) {
* Objects.requireNonNull(consumer);
* final int size = ArrayList.this.size;
* int i = cursor;
* if (i >= size) {
* return;
* }
* final Object[] elementData = ArrayList.this.elementData;
* if (i >= elementData.length) {
* throw new ConcurrentModificationException();
* }
* while (i != size && modCount == expectedModCount) {
* consumer.accept((E) elementData[i++]);
* }
* // update once at end of iteration to reduce heap write traffic
* cursor = i;
* lastRet = i - 1;
* checkForComodification();
* }
*
* 如果有更改則拋出ConcurrentModificationException異常,
*
* final void checkForComodification() {
* 之前的版本不等於,當前的版本,判斷爲數據更新了。
* 那麼?爲什麼產生這樣的判斷呢,因爲你刪除後變爲新數組,來不及
* 更新版本,jvm不知道你當前數據狀態,是否變化,無法再進行遍歷
* if (modCount != expectedModCount)
* throw new ConcurrentModificationException();
* }
* }
*/
/**
* 改進:
*
*/
List<String> forEachIterator = new ArrayList<>();
forEachIterator.add("a");
forEachIterator.add("b");
forEachIterator.add("c");
java.util.Iterator<String> iterator = forEachIterator.iterator();
while (iterator.hasNext()) {
String next = iterator.next();
if ("a".equals(next)) {
iterator.remove();
}
}
這裏:foreach實現的就是迭代器。
補充:倒序循環
/**
* 場景三:
* 應用:倒序遍歷
*/
List<String> forEachIteratorDown = new ArrayList<>();
forEachIteratorDown.add("a");
forEachIteratorDown.add("b");
forEachIteratorDown.add("c");
ListIterator<String> item = forEachIteratorDown.listIterator();
//這裏需要先將指針移向最後一位,再進行倒敘
while (item.hasNext()) {
item.next();
}
while (item.hasPrevious()) {
String previous = item.previous();
System.out.println(previous);
}
源碼:待挖掘,再做記錄備註。