【Java】集合类 - Collection接口的三种遍历方法

Posted by 西维蜀黍 on 2019-03-14, Last Modified on 2023-02-28

第一种 - Iterator(迭代器)

Iterator<String> iterator = list.iterator();  
while(iter.hasNext())  
{  
	iter.next();  
}  

这种方式在循环执行过程中会进行数据锁定,性能稍差。

同时,如果你想在进行遍历的过程中,移除List中的某个元素,只能调用iterator.remove方法,而不能使用list.remove()方法,否则会抛出并发修改异常(java.util.ConcurrentModificationException)。

第二种 - foreach

for(String tmp:list)  
{  
	//do something
}  

foreach只是Java提供的语法糖,内部调用Iterator(第一种),换汤不换药,因此比 Iterator 稍慢。

第三种 - get(i)

int listSize = list.size();
for (int i = 0; i < listSize; i++) {
    list.get(i);
}

内部不锁定,效率最高,但是当写多线程时要考虑并发操作的问题。

Iterator(迭代器)的使用 - Fail fast问题

前面我们提到了,如果你想在使用Iterator(迭代器)进行遍历的过程中,移除List中的某个元素,只能调用iterator.remove方法,而不能调用list.remove()方法,否则一定会抛出并发修改异常(java.util.ConcurrentModificationException)。

注意,当这个异常被抛出时,并不意味着这个list一定正在被多个线程同时使用,而在同一个线程中既一边遍历 list,一边调用list.remove()方法,也会抛出ConcurrentModificationException。

解决办法

可以使用CopyOnWriteArrayList来替换ArrayList。

CopyOnWriteArrayList为何物?

CopyOnWriteArrayList是ArrayList 的一个线程安全的变体,其中所有可变操作(add、set 等等)都是通过对底层数组进行一次新的复制来实现的。

因此,该类产生的开销比较大,但是在两种情况下,它非常适合使用:

  1. 在不能或不想进行同步遍历,但又需要从并发线程中排除冲突时。
  2. 当遍历操作的数量大大超过可变操作的数量时。

遇到这两种情况使用CopyOnWriteArrayList来替代ArrayList再适合不过了。

那么,为什么CopyOnWriterArrayList可以替代ArrayList呢?

  • CopyOnWriterArrayList的无论是从数据结构、定义都和ArrayList一样。它和ArrayList一样,同样是实现List接口,底层使用数组实现。在方法上也包含add、remove、clear、iterator等方法。
  • CopyOnWriterArrayList根本就不会产生ConcurrentModificationException异常,也就是它使用迭代器完全不会产生fail-fast机制。

Reference