理解增强for循环
增强for循环的结构
先看一下正常的写法
private void test1() {
List<String> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i + "");
}
System.out.println("list = " + list);
for (String s : list) {
System.out.println(s);
}
}
再看一下编译出来的.class文件是如何使用增强for循环
private void test1() {
List<String> list = new ArrayList();
for(int i = 0; i < 10; ++i) {
list.add(i + "");
}
System.out.println("list = " + list);
Iterator var4 = list.iterator();
while(var4.hasNext()) {
String s = (String)var4.next();
System.out.println(s);
}
}
这样就可以解释,增强for循环其实就是利用迭代器进行遍历。同样也不难看出增强for循环用于Iterable接口的对象,对于数组的遍历实际上还是普通for循环。
并发修改异常
“java.util.ConcurrentModificationException”,这个异常就不多说了,很常见,看下面代码(必现):
private void test1() {
List<String> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i + "");
}
System.out.println("list = " + list);
for (String s : list) {
if (s.equals("3")) {
list.remove(s);
}
}
}
前面我们知道增强for循环用的是迭代器的结构,这里在增强for循环内部使用了ArrayList的remove函数,就会导致modCount次数加1,如下:
private void fastRemove(int index) {
modCount++; // 这里会将修改次数增加1
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
当这个元素从list中删除后,从增强for循环取出下一个元素时,就会出现ConcurrentModificationException,这是因为如下:
public E next() {
checkForComodification(); // 这里会检查modCount != expectedModCount
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
结果显而易见。
奇怪的现象
我们将一个线程打印ArrayList的元素,一个线程将其赋值为null,看看是否出现Exception:
private void test1() {
for (int i = 0; i < 10; i++) {
list.add(i + "");
}
new Thread(new Runnable() {
@Override
public void run() {
for (String s : list) {
System.out.println(s + ", list = " + list);
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
list = null;
}
}).start();
}
奇怪的是,竟然没有错误,还能打印出每个元素:
0, list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1, list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
2, list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
3, list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
4, list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
5, list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
6, list = null
7, list = null
8, list = null
9, list = null
其实从上面的讲解中,我们已经知道了增强for循环是拿到了ArrayList的iterator,在其源码中会new一个Itr对象,这就相当于Itr持有外部类ArrayList的引用,就算赋值为null,GC也不会回收,除非内部类也被回收。
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData; // 这里就会持有ArrayList的引用
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
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();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
但是这里不建议这种写法,毕竟线程的运行顺序不确认,容易引发NullPointerException。