Java中,一般存在三种遍历方式:
for
foreach
iterator
for循环
修改:遍历中修改,不会存在任何问题
删除:遍历中删除某个元素,集合/数组的长度就会变短,这样在遍历时就有可能造成数组下标越界异常(注意,for循环的list.size每次遍历都会获取,所以如果删除一个中间元素,遍历并不会出现下标越界;下表越界只会发生在同一循环内,比如已经删除了最后一个元素,却又获取最后一个元素的情况)
// 代码示例如下
// 不会抛出异常
for (int i = 0; i < list.size(); i++) {
if (i == 0) {
list.remove(0);
}
System.out.println(list.get(i));
}
// 会抛出数组下标越界
for (int i = 0; i < list.size(); i++) {
if (i == 2) {
list.remove(0);
}
System.out.println(list.get(i));
}
添加:遍历中一直添加元素,有可能造成无穷循环的情况
// 代码示例如下
for (int i = 0; i < list.size(); i++) {
if (i == list.size() - 1) {
list.add("a");
}
}
foreach 与 iterator
foreach 实际上只是 iterator的一个语法糖,如果由如下foreach代码:
for (String item : items) {
System.out.print(item);
}
实际上编译后的代码为:
Iterator iterator = items.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next());
}
修改:使用iter在遍历时修改,与for循环一样,不会发生任何问题
删除:使用iter在遍历时删除元素,如果使用的是iterator.remove()方法是可以正常删除的:
// 情况一: 使用集合提供的remove方法:
for (String s : list) { // 抛出ConcurrentModificationException 抛出并发修改异常,无法删除
if (s.equals("0")) {
list.remove(0);
}
System.out.println(s);
}
// 情况二:删除倒数第二个元素,不会报错,因为下次hasNext返回false直接退出循环
Iteratoriterator = list.iterator();
while (iterator.hasNext()) {
String value = iterator.next();
if ("1".equals(value)) {
list.remove(list.size() - 2); // 删除元素1
}
System.out.println(value); // 0, 1
}
System.out.println(list); // [0, 2]
// 情况三:使用iterator提供的remove方法:
Iteratoriterator = list.iterator();
while (iterator.hasNext()) {
String value = iterator.next();
if ("0".equals(value)) {
iterator.remove(); // 不会抛出异常,会正常删除
}
System.out.println(value); // 还是会打印出被删除的元素 0,1,2
}
System.out.println(list); // 输出的元素确实是删除过后的 [1,2]
增加:增加无论任何情况下,都会抛出ConcurrentModificationException异常
Iterator的源码
参考类: ArrayList$Itr
// 源码如下:
private class Itr implements Iterator {
int cursor; // 下次调用next()获取的元素的指针
int lastRet = -1; // 最后一次操作的索引,如果上次操作为remove,则会置为-1 是一个记录变量
int expectedModCount = modCount; // modCount代表当前列表被修改的次数,使用expectedModCount记录当前状态的修改次数
public boolean hasNext() { // 如果当前指针大小小于list的size大小,说明还有元素没有遍历完
return cursor != size;
}
// 获取下一个元素
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
// 判断是否已经遍历到最后一个元素
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1; // 获取cursor位置元素成功,将指针位置后移一位(cursor++)
return (E) elementData[lastRet = i]; // 使用lastRet记录上一次操作的位置
}
// 移除当前元素
public void remove() {
// 如果上次操作为remove,则抛出非法参数异常,所以循环直接使用iterator.remove() 就会抛出 IllegalStateException
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
// 调用当前集合的remove()方法,移除当前的元素
ArrayList.this.remove(lastRet);
cursor = lastRet; // 指针变为上次操作的索引(删除,数组后面的元素会向前移动,下次遍历的元素位置是coursor+1)
lastRet = -1; // 标记上次操作为remove
expectedModCount = modCount; // 更新当前修改次数
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
// 检查修改次数是否发生变化: 即检测list是否被修改
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}