转载自链接:[https://blog.csdn.net/sinat_28978689/article/details/80724694]
在阅读阿里巴巴规约的时候发现有一条规约是关于List的【不要在foreach里面进行元素的remove/add操作,remove请使用Iterator方式】。然后想起以前自己做项目的时候删除某一元素的逻辑报下标越界错误,那时候记得处理是用一新的List进行存储,然后整体从原List移除所有符合规则的元素,现在做一总结。用这个Iterator主要可以避免下标越界或者遍历是漏掉符合规则的下一个数据。
先拉出来正确删除元素的姿势。正确姿势是利用Iterator的remove方法。具体操作如下:
public static void main(String[] args) {
List<Integer> lists = new ArrayList<Integer>();
lists.add(1);
lists.add(2);
lists.add(3);
lists.add(4);
lists.add(5);
Iterator<Integer> iterator = lists.iterator();
while (iterator.hasNext()){
Integer obj = iterator.next();
if(3==obj || 4==obj){
iterator.remove();
}
}
System.out.println(lists);
}
附上自己比较笨的操作方式:
public static void main(String[] args){
List<Integer> lists = new ArrayList<Integer>();
List<Integer> listscopy = new ArrayList<>();
lists.add(1);
lists.add(2);
lists.add(3);
lists.add(4);
lists.add(5);
int size = lists.size();
for (int i = 0; i < lists.size(); i++) {
if(3==lists.get(i) || 4 == lists.get(i)){
listscopy.add(lists.get(i));
}
}
lists.removeAll(listscopy);
System.out.println(lists);
}
然后常用的错误方式有以下三种:
第一种方式,多出现下标越界问题。这个主要原因是因为我们在循环遍历时,将我们的长度进行定值确定。而忽略掉在满足条件时,list的长度是减少的。
public static void main(String[] args) {
List<Integer> lists = new ArrayList<Integer>();
lists.add(1);
lists.add(2);
lists.add(3);
lists.add(4);
lists.add(5);
int size = lists.size(); // 定长设置,会造成元素下标越界,如果将for中变量直接换成lists.size()可以吗?
for (int i = 0; i < size; i++) {
if(3==lists.get(i) || 4 == lists.get(i)){
lists.remove(i); //lists.remove(lists.get(i));
}
}
System.out.println(lists);
}
第二种方式也就是上面的注释所说,既然会出现下标越界,那我就利用动态的大小不就可以了,但是利用这种方法会产生另外一种情况,就是会忽略掉remove元素后面的一个元素,这个是因为删除符合规则的元素后,list长度减一,而同时后面的元素往前补一位,造成当前i值下对应两个元素。
public static void main(String[] args) {
List<Integer> lists = new ArrayList<Integer>();
lists.add(1);
lists.add(2);
lists.add(3);
lists.add(4);
lists.add(5);
int size = lists.size();
for (int i = 0; i < lists.size(); i++) {
if(3==lists.get(i) || 4 == lists.get(i)){
lists.remove(i); //lists.remove(lists.get(i));
}
}
System.out.println(lists); // 打印[1, 2, 4, 5] 此时会将4忽略掉
}
第三种方式是利用增强FOR循环造成的。这种方式会直接给报错:
ERRORInfo:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
public static void main(String[] args) {
List<Integer> lists = new ArrayList<Integer>();
lists.add(1);
lists.add(2);
lists.add(3);
lists.add(4);
lists.add(5);
for (Integer i : lists){
if(3==i || 4==i){
lists.remove(i);
}
}
System.out.println(lists);
}
这种情况下。主要是集合遍历是使用Iterator, Iterator是工作在一个独立的线程中,并且拥有一个互斥锁。Iterator 被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast原则 Iterator 会马上抛出java.util.ConcurrentModificationException 异常。所以 Iterator 在工作的时候是不允许被迭代的对象被改变的。
【 文章有查询:高爽|Coder,原文地址:http://blog.csdn.net/ghsau/article/details/9347357。】