小菜们肯定写过这样的代码:
[java] view plain copy
1. for(List list:lists){
2.
3. //判断,如果成立删除
4.
5. if(true){lists.remove(list);}
6.
7. }
这种错误很明显,如果for循环的时候是不允许删除这个list对象的。
这个相必大家都知道为什么会这样,因为ArrayList的父类AbstractList里有个modCount的字段记录着List的总数,for循环的时候如果增加或者删除了元素,(修改不会影响),此字段会变化,那么在下次for循环的时候检查到跟之前的长度不同,此时会报ConcurrentModificationException异常。
解决办法有两种:
1,
[java] view plain copy
1. Iterator it=lists.iterator();
2.
3. while(it.hasNext()){
4.
5. it.next();
6.
7. it.remove();
8.
9. }
即先把ArrayList转换成Iterator,然后while循环删除。这种在项目中很常见。
2,
[java] view plain copy
1. for(List list:lists.clone()){
2.
3. //判断,如果成立删除
4.
5. if(true){lists.remove(list);}
6.
7. }
第二种是克隆的方法, 其本质和Iterator相同(我自己认为的,下面看我的解释)。
测试克隆的时候发现假如我这样List list2=lists.clone();查看list和list2这两个对象,发现hashCode一样,如果改成这样List list2=lists;
即直接赋值,hashCode也一样,这是因为ArrayList的hashCode()方法一样,hashCOde一样,但是否就可以以为这三个指向的都是同一个对象呢?
介绍clone原理之前先介绍下Java在内存中的存值方式:
(下面这段从其他地方烤的)
Java把内存划分成两种:一种是栈内存,一种是堆内存。
在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配。
堆内存用来存放由new创建的对象和数组。
在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理
假如我们先List lists=new ArrayList();lists.add(obj1);lists。add(obj2)
在内存中把lists放入栈内存 把obj放入堆内存 我们在调用clone方法的时候,并不是直接把list2的对象直接指向obj1 和obj2的堆内存地址,而是指向obj1 obj2实际的值
比如堆中是这样的顺序 obj1 name1 age1 obj2 name2 age2 ,克隆后 的list是直接指向name1 age1 name2 age2 而不是obj1 obj2!!
如果我们是直接赋值 List list2=lists; 这种情况 list2 和lists都是指向obj1 和obj2!!
这样大家应该很清楚克隆是怎么回事的吧