android hashmap remove删不掉_Java 遍歷HashMap並修改(remove)

遍歷HashMap的方法有多種,比如通過獲取map的keySet, entrySet, iterator之后,都可以實現遍歷,然而如果在遍歷過程中對map進行讀取之外的操作則需要注意使用的遍歷方式和操作方法。

public class MapIteratorTest {

private static Map map = new HashMap();

public static void main(String[] args) {

//init

for(int i = 0; i < 10; i++){

map.put(i, "value" + i);

}

for(Map.Entry entry : map.entrySet()){

Integer key = entry.getKey();

if(key % 2 == 0){

System.out.println("To delete key " + key);

map.remove(key);

System.out.println("The key " + + key + " was deleted");

}

}

System.out.println("map size = " + map.size());

for(Map.Entry entry : map.entrySet()){

System.out.println( entry.getKey() +" = " + entry.getValue());

}

}

}

上面代碼的輸出結果為

To delete key 0

The key 0 was deleted

Exception in thread "main" java.util.ConcurrentModificationException

at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793)

at java.util.HashMap$EntryIterator.next(HashMap.java:834)

at java.util.HashMap$EntryIterator.next(HashMap.java:832)

at com.gpzuestc.collection.MapIteratorTest.main(MapIteratorTest.java:60)

通過上面的輸出可以發現第一個偶數key元素已經被成功remove,異常的拋出位置是在迭代器遍歷下一個元素的時候。

如果把上面高亮的遍歷代碼替換成keySet的方式,通過keySet的remove操作同樣會在遍歷下個元素時拋出異常,示例如下。

12345678

SetkeySet=map.keySet();for(Integerkey:keySet){if(key%2==0){System.out.println("To delete key "+key);keySet.remove(key);System.out.println("The key "++key+" was deleted");}}

123456

Todeletekey0Thekey0wasdeletedExceptioninthread"main"java.util.ConcurrentModificationExceptionatjava.util.HashMap$HashIterator.nextEntry(HashMap.java:793)atjava.util.HashMap$KeyIterator.next(HashMap.java:828)atcom.gpzuestc.collection.MapIteratorTest.main(MapIteratorTest.java:49)

如果要實現遍歷過程中進行remove操作,上面兩種方式都不能使用,而是需要通過顯示獲取keySet或entrySet的iterator來實現。

1234567891011

Iterator>it=map.entrySet().iterator();while(it.hasNext()){Map.Entryentry=it.next();Integerkey=entry.getKey();if(key%2==0){System.out.println("To delete key "+key);it.remove();System.out.println("The key "++key+" was deleted");}}

12345678910111213141516

Todeletekey0Thekey0wasdeletedTodeletekey2Thekey2wasdeletedTodeletekey4Thekey4wasdeletedTodeletekey6Thekey6wasdeletedTodeletekey8Thekey8wasdeletedmapsize=51=value13=value35=value57=value79=value9

分析原因

其實上面的三種遍歷方式從根本上講都是使用的迭代器,之所以出現不同的結果是由於remove操作的實現不同決定的。

首先前兩種方法都在調用nextEntry方法的同一個地方拋出了異常

1234567

finalEntrynextEntry(){if(modCount!=expectedModCount)thrownewConcurrentModificationException();Entrye=next;......}

這里modCount是表示map中的元素被修改了幾次(在移除,新加元素時此值都會自增),而expectedModCount是表示期望的修改次數,在迭代器構造的時候這兩個值是相等,如果在遍歷過程中這兩個值出現了不同步就會拋出ConcurrentModificationException異常。

1、HashMap的remove方法實現

1234

publicVremove(Objectkey){Entrye=removeEntryForKey(key);return(e==null?null:e.value);}

2、HashMap.KeySet的remove方法實現

public boolean remove(Object o) {

return HashMap.this.removeEntryForKey(o) != null;

}

3、HashMap.HashIterator的remove方法實現

12345678910

publicvoidremove(){if(current==null)thrownewIllegalStateException();if(modCount!=expectedModCount)thrownewConcurrentModificationException();Objectk=current.key;current=null;HashMap.this.removeEntryForKey(k);expectedModCount=modCount;}

以上三種實現方式都通過調用HashMap.removeEntryForKey方法來實現刪除key的操作。在removeEntryForKey方法內只要移除了key modCount就會執行一次自增操作,此時modCount就與expectedModCount不一致了,上面三種remove實現中,只有第三種iterator的remove方法在調用完removeEntryForKey方法后同步了expectedModCount值與modCount相同,所以在遍歷下個元素調用nextEntry方法時,iterator方式不會拋異常。

1234567891011121314151617181920212223242526

finalEntryremoveEntryForKey(Objectkey){inthash=(key==null)?0:hash(key.hashCode());inti=indexFor(hash,table.length);Entryprev=table[i];Entrye=prev;while(e!=null){Entrynext=e.next;Objectk;if(e.hash==hash&&((k=e.key)==key||(key!=null&&key.equals(k)))){modCount++;size--;if(prev==e)table[i]=next;elseprev.next=next;e.recordRemoval(this);returne;}prev=e;e=next;}returne;}

發散

1、如果是遍歷過程中增加或修改數據呢?

增加或修改數據只能通過Map的put方法實現,在遍歷過程中修改數據可以,但如果增加新key就會在下次循環時拋異常,因為在添加新key時modCount也會自增。

2、有些集合類也有同樣的遍歷問題,如ArrayList,通過Iterator方式可正確遍歷完成remove操作,直接調用list的remove方法就會拋異常。

//會拋ConcurrentModificationException異常for(String str : list){

list.remove(str);

}

//正確遍歷移除方式

Iterator it = list.iterator();

while(it.hasNext()){

it.next();

it.remove();

}

3、jdk為什么這樣設計,只允許通過iterator進行remove操作?

HashMap和keySet的remove方法都可以通過傳遞key參數刪除任意的元素,而iterator只能刪除當前元素(current),一旦刪除的元素是iterator對象中next所正在引用的,如果沒有通過modCount、 expectedModCount的比較實現快速失敗拋出異常,下次循環該元素將成為current指向,此時iterator就遍歷了一個已移除的過期數據。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值