HashMap和List遍歷方法及如何遍歷刪除元素總結(jié)
相信大家對集合遍歷再熟悉不過了,這里總結(jié)一下HashMap和List的遍歷方法,以及它們該如何實(shí)現(xiàn)遍歷刪除。
這里對于每種遍歷刪除出現(xiàn)的問題的原因都給出了詳解!
(一)List的遍歷方法及如何實(shí)現(xiàn)遍歷刪除
我們造一個(gè)list出來,接下來用不同方法遍歷刪除,如下代碼:
List<String> list= new ArrayList<String>();
famous.add("zs");
famous.add("ls");
famous.add("ww");
famous.add("dz");
1、for循環(huán)遍歷list:
for(int i=0;i<list.size();i++){
if(list.get(i).equals("ls"))
list.remove(i);
}
這是一種很常見的遍歷方式,但是使用這種遍歷刪除元素會出現(xiàn)問題,原因在于刪除某個(gè)元素后,list的大小發(fā)生了變化,而你的索引也在變化,所以會導(dǎo)致你在遍歷的時(shí)候漏掉某些元素。比如當(dāng)你刪除第一個(gè)元素后,繼續(xù)根據(jù)索引訪問第二個(gè)元素后,因?yàn)閯h除的原因,后面的元素都往前移動了以為,所以實(shí)際訪問的是第三個(gè)元素。因此,這種遍歷方式可以用在讀取元素,而不適合刪除元素。
2、增強(qiáng)for循環(huán):
for(String x:list){
if(x.equals("ls"))
list.remove(x);
}
這也是一種很常見的遍歷方式,但是使用這種遍歷刪除元素也會出現(xiàn)問題,運(yùn)行時(shí)會報(bào)ConcurrentModificationException異常
其實(shí)增強(qiáng)for循環(huán)是java語法糖的一種體現(xiàn),如果大家通過反編譯得到字節(jié)碼,那么上面這段代碼的內(nèi)部實(shí)現(xiàn)如下所示:
for(Iterator<String> it = list.iterator();it.hasNext();){
String s = it.next();
if(s.equals("madehua")){
list.remove(s);
}
}
下面就解釋為什么會報(bào)ConcurrentModificationException異常。分析Iterator的源代碼,重點(diǎn)分析整個(gè)調(diào)用該過程中的
函數(shù)(hasNext和remove):
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size; // size為集合中元素的個(gè)數(shù)
}
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
/* 此方法并沒被調(diào)用,只是調(diào)用List.remove方法
public void remove() {
checkForComodification();
try {
ArrayList.this.remove(lastRet); // size字段減1
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
*/
final void checkForComodification() { // 檢查修改和當(dāng)前版本號是否一致,不一致則拋出異常
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
// List.remove
@Override public boolean remove(Object object) {
Object[] a = array;
int s = size;
if (object != null) {
for (int i = 0; i < s; i++) {
if (object.equals(a[i])) {
System.arraycopy(a, i + 1, a, i, --s - i);
a[s] = null; // Prevent memory leak
size = s;
modCount++; // 核心代碼:修改了版本號。這樣當(dāng)checkForComodification的時(shí)候,modCount值就和expectedModCount不同
return true;
}
}
} else {
for (int i = 0; i < s; i++) {
if (a[i] == null) {
System.arraycopy(a, i + 1, a, i, --s - i);
a[s] = null; // Prevent memory leak
size = s;
modCount++;
return true;
}
}
}
return false;
}
接下來梳理一下流程,這時(shí)候你就會發(fā)現(xiàn)這個(gè)異常是在next方法的checkForComodification中拋出的。拋出的原因是
modCount !=expectedModCount。這里的modCount是指這個(gè)list對象從呢我出來到現(xiàn)在被修改的次數(shù),當(dāng)調(diào)用list
的add或者remove方法的時(shí)候,這個(gè)modCount都會自動增減;iterator創(chuàng)建的時(shí)候modCount被復(fù)制給了
expectedModcount,但是調(diào)用list的add和remove方法的時(shí)候不會同時(shí)自動增減expectedModcount,這樣就導(dǎo)致
兩個(gè)count不相等,從而拋出異常。大家如果理解了上面的執(zhí)行流程,以后碰到類似這種問題,比如如果刪除的是倒數(shù)
第二個(gè)元素卻不會碰到異常。就會知道為什么了。
3、iterator遍歷刪除
Iterator<String> it = list.iterator();
while(it.hasNext()){
String x = it.next();
if(x.equals("del")){
it.remove();
}
}
這種方式是可以正常遍歷和刪除的。但是你可能看到上面代碼感覺和增強(qiáng)for循環(huán)內(nèi)部實(shí)現(xiàn)的代碼差不多,其實(shí)差別就在于上面使用一個(gè)使用list.remove(),一個(gè)使用it.remove()。
(二)HashMap的遍歷刪除及如何實(shí)現(xiàn)遍歷刪除
一樣我們先造一個(gè)hashmap出來,如下
private static HashMap<Integer, String> map = new HashMap<Integer, String>();;
public static void main(String[] args) {
for(int i = 0; i < 10; i++){
map.put(i, "value" + i);
}
}
1、第一種遍歷刪除:
for(Map.Entry<Integer, String> 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");
}
這種遍歷刪除依舊會報(bào)ConcurrentModificationException異常,
2、第二種遍歷刪除:
Set<Integer> keySet = map.keySet();
for(Integer key : keySet){
if(key % 2 == 0){
System.out.println("To delete key " + key);
keySet.remove(key);
System.out.println("The key " + + key + " was deleted");
}
}
這種遍歷刪除依舊會報(bào)ConcurrentModificationException異常,
3、第三種遍歷刪除:
Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator();
while(it.hasNext()){
Map.Entry<Integer, String> entry = it.next();
Integer key = entry.getKey();
if(key % 2 == 0){
System.out.println("To delete key " + key);
it.remove();
System.out.println("The key " + + key + " was deleted");
}
}
這種遍歷是OK的
分析上述原因,如果大家理解了List的遍歷刪除,那么感覺HashMap的遍歷刪除是不是有類似之處啊。下面就分析一下原因:
如果查詢源代碼以上的三種的刪除方式都是通過調(diào)用HashMap.removeEntryForKey方法來實(shí)現(xiàn)刪除key的操作。
在removeEntryForKey方法內(nèi)知識一場了key modCount就會執(zhí)行一次自增操作,此時(shí)modCount就與expectedModCOunt不一致了
,上面三種remove實(shí)現(xiàn)中,只有第三種iterator的remove方法在調(diào)用完removeEntryForKey方法后同步了expectedModCount值與
modCount相同,所以iterator方式不會拋出異常。最后希望大家遇到問題到查詢源代碼,它會給你最好的解釋!
---------------------
作者:demohui
來源:CSDN
原文:https://blog.csdn.net/demohui/article/details/77748809
版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請附上博文鏈接!
相關(guān)文章
java實(shí)現(xiàn)飛機(jī)大戰(zhàn)游戲
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)飛機(jī)大戰(zhàn)游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-03-03
java實(shí)用驗(yàn)證碼的實(shí)現(xiàn)代碼
這篇文章主要為大家介紹了java實(shí)用驗(yàn)證碼的實(shí)現(xiàn)代碼,驗(yàn)證碼實(shí)際上就是隨機(jī)選擇一些字符以圖片的形式展現(xiàn)在頁面上,感興趣的小伙伴們可以參考一下2016-03-03
詳解Spring Boot中整合Sharding-JDBC讀寫分離示例
這篇文章主要介紹了詳解Spring Boot中整合Sharding-JDBC讀寫分離示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-03-03
Springboot如何使用filter對request body參數(shù)進(jìn)行校驗(yàn)
這篇文章主要介紹了Springboot如何使用filter對request body參數(shù)進(jìn)行校驗(yàn),具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
springboot中@RestController注解實(shí)現(xiàn)
在JavaWeb開發(fā)中,Spring框架及其組件SpringMVC因高效和強(qiáng)大功能而廣受歡迎,@RestController注解是SpringMVC中的重要組成部分,下面就來介紹一下,感興趣的可以了解一下2024-09-09

