/** 
 * 一个ArrayList对象aList中存有若干个字符串元素, 
 * 现欲遍历该ArrayList对象,删除其中所有值为”abc”的字符串元素 
 */ 
 import java.util.*; 
 public class Test3 { 
 public static void main(String[] args) { 
 ArrayList aList=new ArrayList(); 
 aList.add(“bbc”); 
 aList.add(“abc”); 
 aList.add(“ysc”); 
 aList.add(“abc”) 
 System.out.println(“移除前:”+aList); 
 Iterator it=aList.iterator(); 
 while(it.hasNext()){ 
 if(“abc”.equals(it.next())) 
 it.remove(); } 
 System.out.println(“移除后:”+aList); } 
 }

1、判断一个数组是否包含某个值

在判断一个数组中是否包含某个值的时候,开发者经常这样做:

Set<String> set = new HashSet<String>(Arrays.asList(arr));
return set.contains(targetValue);

在 在Java中如何高效的判断数组中是否包含某个元素
一文中,深入分析过,以上方式虽然可以实现功能,但是效率却比较低。因为将数组压入Collection类型中,首先要将数组元素遍历一遍,然后再使用集合类做其他操作。

在判断一个数组是否包含某个值的时候,推荐使用for循环遍历的形式或者使用Apache Commons类库中提供的 ArrayUtils 类的 contains 方法。

2、在循环中删除列表中的元素

在讨论这个问题之前,先考虑以下代码的输出结果:

ArrayList<String> list = new ArrayList<String>(Arrays.asList("a","b","c","d"));
for(int i=0;i<list.size();i++){
    list.remove(i);
}
System.out.println(list);

输出结果:

[b,d]

以上代码的目的是想遍历删除list中所有元素,但是结果却没有成功。原因是忽略了一个关键的问题:当一个元素被删除时,列表的大小缩小并且下标也会随之变化,所以当你想要在一个循环中用下标删除多个元素的时候,它并不会正常的生效。

也有些人知道以上代码的问题就由于数组下标变换引起的。所以,他们想到使用增强for循环的形式:

ArrayList<String> list = new ArrayList<String>(Arrays.asList("a","b","c","d"));
for(String s:list){
    if(s.equals("a")){
        list.remove(s);
    }
}

但是,很不幸的是,以上代码会抛出 ConcurrentModificationException ,有趣的是,如果在remove操作后增加一个break,代码就不会报错:

ArrayList<String> list = new ArrayList<String>(Arrays.asList("a","b","c","d"));
for(String s:list){
    if(s.equals("a")){
        list.remove(s);
        break;
    }
}

在 Java中的fail-fast机制
一文中,深入分析了几种在遍历数组的同时删除其中元素的方法以及各种方法存在的问题。其中就介绍了上面的代码出错的原因。

迭代器(Iterator)是工作在一个独立的线程中,并且拥有一个 mutex 锁。 迭代器被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 迭代器会马上抛出 java.util.ConcurrentModificationException 异常。

所以,正确的在遍历过程中删除元素的方法应该是使用Iterator:

ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
    String s = iter.next();

    if (s.equals("a")) {
        iter.remove();
    }
}

next() 方法必须在调用 remove() 方法之前调用。如果在循环过程中先调用 remove() ,再调用 next() ,就会导致异常 ConcurrentModificationException 。原因如上。