清空ArrayList所引發的思考
ArrayList<String> list = new ArrayList<String>();
list.add("a111");
list.add("b111");
list.add("c111");
list.add("e111");
list.add("f111");
list.add("g111");
想辦法把list清空,猛一看,這還不簡單,so easy ,直接上代碼:
for(int i=0;i<list.size();i++) {
list.remove(i);
}
執行後才會發現這樣寫有問題,執行結果:
操作前:[a111, b111, c111, e111, f111, g111]
操作後:[b111, e111, g111]
為什麼呢?明明看着可以的啊,為什麼沒有清空啊,問題出在哪呢?
經過一番研究,發現執行循環的時候每次都要調用list.size()方法,由于下面有list.remove(i)操作,是以list.size()是不斷變化的,是以會出現上面的情況
由此想到解決方法:把list.size()賦給一個變量,這樣就不會動态變化了,看代碼:
int listsize = list.size();
for(int i=0;i<listsize;i++) {
list.remove(i);
}
心想這樣總行了吧,把list.size()專門列出來了,再不行也太難伺候了,執行後才發現還真不行,而且會報錯:
操作前:[a111, b111, c111, e111, f111, g111]
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 3, Size: 3
at java.util.ArrayList.RangeCheck(ArrayList.java:547)
at java.util.ArrayList.remove(ArrayList.java:387)
at com.autonavi.test.TestListRemove.main(TestListRemove.java:35)
數組越界異常,哪地方越界了呢,不可能啊,listsize是不變的,每次list.remove(i)怎麼會報越界異常呢?
打開源碼,一步一步檢視,看看到底是在哪一步報錯的,經研究才發現:
list是不斷變化的,list.remove(i)并不是都是删掉的第一個元素,動态數組跟靜态數組最大的差別就是動态數組的存儲空間是不斷變化的,而靜态數組是一成不變的
比方說,循環執行第一次的時候,i=0,list.remove(0),把第一個元素a111删掉了,這時候list隻有5個元素了,下标隻能是0~4,[b111, c111, e111, f111, g111]
執行第二次的時候,i=1,list.remove(1),把此時list的第二個元素c111删掉了,注意不是删的b111,而是删的c111,這時候list還有4個元素,下标隻能是0~3,[b111, e111, f111, g111]
執行第三次的時候,i=2,list.remove(2),把此時list的第三個元素f111删掉了,這時候list還有3個元素,下标隻能是0~2,[b111, e111, g111]
for循環執行條件是i<listsize; listsize是剛開始list的size(),值為6,是以還會繼續執行下去,這時候就會出現問題了:
執行第四次的時候,i=3,執行list.remove(3)操作,由于目前list隻有3個元素,下标隻能是0~2,是以執行到這的時候會報越界異常。
那到底該怎麼辦呢?難道一個小小的ArrayList還不能把它清空了,不要急,利用疊代就可以實作:
Iterator<String> it = list.iterator();
for(;it.hasNext();) {
it.next();
it.remove();
}
注意隻有這一種方式才可以在周遊 list的時候執行删除操作,其他方式都不可以實作,會報:ConcurrentModificationException異常
執行結果:
操作前:[a111, b111, c111, e111, f111, g111]
操作後:[]
還可以調用list的clear()方法實作清空list,也是最簡單的一種形式:
list.clear();
執行結果:
操作前:[a111, b111, c111, e111, f111, g111]
操作後:[]
總結:如要是要清空list,用clear()方法,如果是根據條件删除某一個元素,利用疊代實作,裡面加個if斷判,滿足條件的執行it.remove()操作即可
1.這樣給List指派是很危險的:
List<Channel> notUseChannelList = onUsedChannelList;
因為List是一個對象,這時改變notUseChannelList的值也會改變onUsedChannelList的值,它們指向堆記憶體中同一塊位址
如果有特殊要求,可以像上面那樣指派
一般是利用List的addAll方法,把一個List的值加載到另外一個List中去。
2.這樣寫是肯定有問題的:
for(Channel channelTemp: notUseChannelList) {
System.out.println("&&&&&&&: " + channelTemp.getChannelId());
for(;it.hasNext();) {
int channelId = it.next().getChannelId();
System.out.println("***it.next().getChannelId()***: " + channelId);
if(channelId == channelTemp.getChannelId()) {
it.remove();
}
}
}
因為周遊一遍後it.hasNext()和it.next()的值都改變了,不能再重複了。
應該改成這樣:
/**
* 可為目前使用者配置設定的頻道
*/
List<Channel> isUseChannelList = new ArrayList<Channel>();
List<Channel> notUseChannelList = new ArrayList<Channel>();
notUseChannelList.addAll(onUsedChannelList);
Iterator<Channel> it = channelList.iterator();
for(;it.hasNext();) {
int channelId = it.next().getChannelId();
for(Channel channelTemp: notUseChannelList) {
if(channelId == channelTemp.getChannelId()) {
it.remove();
}
}
}
主要是記住兩點:
1.像下面這樣指派類似于指針操作,一榮俱榮,要想隻實作簡單的指派操作用addAll方法。
List<Channel> notUseChannelList = onUsedChannelList;
2.it.next()也相當于一個指針,回到末尾後不可能再重新來過了,隻有一次機會。