遍历List的多种方式
在讲如何线程安全地遍历List之前,先看看通常我们遍历一个List会采用哪些方式。
方式一:
for(int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
方式二:
Iterator iterator = list.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
方式三:
for(Object item : list) {
System.out.println(item);
}
方式四(Java 8):
list.forEach(new Consumer<Object>() {
@Override
public void accept(Object item) {
System.out.println(item);
}
});
方式五(Java 8 Lambda):
list.forEach(item -> {
System.out.println(item);
});
方式一的遍历方法对于RandomAccess接口的实现类(例如ArrayList)来说是一种性能很好的遍历方式。但是对于LinkedList这样的基于链表实现的List,通过list.get(i)
获取元素的性能差。
方式二和方式三两种方式的本质是一样的,都是通过Iterator迭代器来实现的遍历,方式三是增强版的for循环,可以看作是方式二的简化形式。
方式四和方式五本质也是一样的,都是使用Java 8新增的forEach方法来遍历。方式五是方式四的一种简化形式,使用了Lambda表达式。
遍历List的同时操作List会发生什么?
先用非线程安全的ArrayList做个试验,用一个线程遍历List,遍历的同时另一个线程删除List中的一个元素,代码如下:
public static void main(String[] args) {
// 初始化一个list,放入5个元素
final List<Integer> list = new ArrayList<>();
for(int i = 0; i < 5; i++) {
list.add(i);
}
// 线程一:通过Iterator遍历List
new Thread(new Runnable() {
@Override
public void run() {
for(int item : list) {
System.out.println(