- 说明 java的for语句增强 是java给我们提供的语法糖 ,原理是使用了迭代器 Iterator 所以for增强 可以看成是Iterator迭代器遍历
基于jdk1.7 测试:
package com.test;
import java.util.ArrayList;
import java.util.List;
public class MyTest {
public static void main(String[] args) throws InterruptedException {
List<String> lists = new ArrayList<>();
lists.add("test1");
lists.add("test2");
lists.add("test3");
lists.add("test4");
for (String k : lists) {
if ("test2".equals(k)) {
lists.remove(k);
}
}
/*
* Iterator<String> it = lists.iterator(); while (it.hasNext()) { String k =
* it.next(); if ("测试2".equals(k)) { lists.remove(k); } }
*/
}
}
生成的class 进行反编译 反编译工具使用的是jd-gui.exe
反编译结果:
package com.test;
import java.util.ArrayList;
import java.util.List;
public class MyTest
{
public static void main(String[] paramArrayOfString)
throws InterruptedException
{
ArrayList localArrayList = new ArrayList();
localArrayList.add("test1");
localArrayList.add("test2");
localArrayList.add("test3");
localArrayList.add("test4");
for (String str : localArrayList) {
if ("test2".equals(str)) {
localArrayList.remove(str);
}
}
}
}
- 为什么迭代器遍历会抛出异常呢?
//代码
public static void main(String[] args) throws InterruptedException {
List<String> lists = new ArrayList<>();
lists.add("test1");
lists.add("test2");
lists.add("test3");
lists.add("test4");
Iterator<String> it = lists.iterator();
while (it.hasNext()) {
String k = it.next();
if ("test2".equals(k)) {
lists.remove(k);
}
}
}
modCount是什么呀?
modCount是修改次数 add remove的时候 这个值就会++
//增加元素 这里只关注ensureExplicitCapacity的modCount++ 其他知识不关注
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//删除元素 modCount++
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
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
return oldValue;
}
迭代器与modCount有什么关系呀?
ArrayList获取iterator:
//Itr 是 ArrayList的内部类
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int cursor; // 下一个要返回元素的下表
int lastRet = -1; //上一个返回的元素下表 这个属性在remove方法中会用到 如果没有值为-1 (默认)
//期待的修改数expectedModCount 赋值为modCount 这个是异常关键 当他俩不相等的时候 就会抛出异常 看方法 checkForComodification
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
//获取下一个值
public E next() {
//检测expectedModCount 和 modCount 如果不相等就抛出异常
//也就是说在迭代器遍历的过程中 如果数组进行了remove或者add 等能使modCount 变化的操作 那!!!! 就会抛出异常 告诉用户 可能有多个线程在操作这个数组 可能会存在数据问题
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;
return (E) elementData[lastRet = i];
}
//注意! 这个remove方法 删除元素之后 还会重新 expectedModCount = modCount; 所以这个不会抛出异常
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();
}
}
//modCount != expectedModCount 抛出异常
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
总结:
也就是在 expectedModCount 不等于 modCount的情况下会抛异常
平时应该怎么做呢?
- 用上边介绍到的Iterator的自身的remove方法
Iterator<String> it = lists.iterator();
while (it.hasNext()) {
String k = it.next();
if ("test2".equals(k)) {
it.remove();
}
}
- 普通的for循环
for(int i=0;i<lists.size();i++) {
if("test2".equals(lists.get(i))) {
lists.remove(i);
}
}
- Java8提供的流操作
lists = lists.stream().filter(k -> !"test2".equals(k)).collect(Collectors.toList());
System.out.println(lists);
- 直接用fail-safe类 java并发包中的类
ConcurrentLinkedDeque 这个是用的链表 没有比较modCount
public static void main(String[] args) throws InterruptedException {
ConcurrentLinkedDeque<String> lists = new ConcurrentLinkedDeque<>();
lists.add("test1");
lists.add("test2");
lists.add("test3");
lists.add("test4");
Iterator<String> it = lists.iterator();
while (it.hasNext()) {
String k = it.next();
if ("test2".equals(k)) {
it.remove();
}
}
System.out.println(lists);
}
public boolean hasNext() {
return nextItem != null;
}
public E next() {
E item = nextItem;
if (item == null) throw new NoSuchElementException();
advance();
return item;
}
private void advance() {
lastRet = nextNode;
Node<E> p = (nextNode == null) ? startNode() : nextNode(nextNode);
for (;; p = nextNode(p)) {
if (p == null) {
// p might be active end or TERMINATOR node; both are OK
nextNode = null;
nextItem = null;
break;
}
E item = p.item;
if (item != null) {
nextNode = p;
nextItem = item;
break;
}
}
}
大总结:
我们使用的增强for循环,其实是Java提供的语法糖,其实现原理是借助Iterator进行元素的遍历。
但是如果在遍历过程中,不通过Iterator,而是通过集合类自身的方法对集合进行添加/删除操作。那么在Iterator进行下一次的遍历时(next方法),经检测发现有一次集合的修改操作并未通过自身进行,那么可能是发生了并发被其他线程执行的,这时候就会抛出异常,来提示用户可能发生了并发修改,这就是所谓的fail-fast机制。
当然还是有很多种方法可以解决这类问题的。比如使用普通for循环、使用Iterator进行元素删除、使用Stream的filter、使用fail-safe的类等。
特别感谢:https://www.jianshu.com/p/61c8750edc3d