详解遍历集合和遍历集合时删除集合元素
集合遍历有多种方式,但各种方式执行效率上稍有差别,遍历集合时删除元素处理不当会有一些问题,这里详细汇总一下。
遍历集合元素的方式主要有以下几种:
这里以ArrayList为例来测试以上几种方式。
先创建一个集合元素类。
再创建一个遍历集合的测试类:
在我的 i5-6500 CPU电脑上多次测试取遍历操作耗时的平均值,得出这几种方法的遍历速度从快到慢依次为:
所以 如果遍历一个集合中元素,建议优先使用Java 8为Iterable接口提供的forEach默认方法。如果你还未使用Java 8,则建议优先使用Iterator接口的hasNex和next方法来实现遍历 。
遍历集合删除集合元素的方式有以下几种:
这里以ArrayList为例来测试以上几种方式。
在我的电脑上多次测试取耗时的平均值,得出这几种方法的遍历速度从快到慢依次为:
其中,iteratorRemove和forRemoveNoSkipping的测试结果很接近,大家可以自行修改集合大小的常量亲自测试,如有问题欢迎反馈。
所以 如果遍历一个集合时删除其中的元素,建议优先使用Java 8提供的流式API来筛选集合元素。如果你还未使用Java 8,则建议优先使用逆序的一般for循环来实现遍历时删除集合元素 。
许多初学者容易使用上面示例中的前三种方式来在遍历集合时删除集合元素,但是得不到正确的结果,原因已经在这三种方法的注释中说明了。对于使用for-each循环时抛出ConcurrentModificationException异常的原因可通过查看ArrayList.remove()方法的源码来探明。for-each循环List集合时使用了一个实现了Iterator接口的ArrayList内部类对象来实现遍历,该内部类源码如下:
使用for-each遍历时调用该内部类的next方法,进而调用该方法中第一行的checkForComodification方法,ConcurrentModificationException异常就是在这个checkForComodification方法中抛出的:
当我们显式调用remove方法来删除集合中的元素时会修改modCount的值,使其与expectedModCount不一致:
官方教程 也有说在以下情况中可以使用Iterator来代替for-each循环: