清空ArrayList所引发的思考(List相关操作)

来源:互联网 发布:怎么改变网络vp6 编辑:程序博客网 时间:2024/06/06 17:32

最近在帮同事做个小程序时,需要删除ArrayList中的数据,但是逻辑明显没错的情况下,老是得不到最后想要的结果,虽然后来找到了原因,但一直没法解决,最后在一个机缘巧合的情况下,看到这篇文章,正好解除了我心中的疑惑,转过来同大家分享

原文地址:http://blog.csdn.net/shanliangliuxing/article/details/6858841,很感谢那篇文章的作者


正文开始(文章有点长,但只要耐心看完,我保证你能有所收获):

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()也相当于一个指针,回到末尾后不可能再重新来过了,只有一次机会。