Java遍历中修改对象——foreach语句的局限性
在lab2中想要实现ConcreteEdgesGraph类的remove方法,使用如下的代码,想在list中进行遍历,查找并删除与某个Vertex(String)相关的Edge。
public class ConcreteEdgesGraph<L> implements Graph<L> {
private final Set<L> vertices = new HashSet<>();
private final List<Edge<L>> edges = new ArrayList<>();
//……………………
/**
* Get all the vertices in this graph.
*
* @return the set of labels of vertices in this graph
*/
@Override public boolean remove(L vertex) {
if(vertices.contains(vertex))
{
vertices.remove(vertex);
for(Edge listEdge:edges)
{
if(listEdge.getSource().equals(vertex)||listEdge.getTarget().equals(vertex))
{
edges.remove(listEdge);
}
}
checkRep();
return true;
}
return false;
//throw new RuntimeException("not implemented");
}
//……………………
}
意为在list中查找到与vertex有关联的Edge listEdge 就直接list调用remove方法在list中删除之。
发现报错,异常类型java.util.ConcurrentModificationException
根据12两篇博客提供的源代码,阅读源码后,总结foreach的机制如下:
1.foreach循环本质上使用java迭代器作为循环工具
2.循环的实现依赖迭代器调用迭代器方法hasNext()与next(),分别判断是否有下一个元素和返回下一个元素并转移至下一元素位置
3.迭代器的实现中引用集合修改次数的变量modCount,创建了自己期望的修改次数变量expectedModCount,初值为modCount。每一轮循环(调用next)都会对两者是否相等进行检查。
4.若使用edges.remove()方法删除list其中一个元素,modCount会+1,exceptedModCount不变,导致异常。(特殊情况,删除倒数第二个元素,由于计数变量curors不变,list 的size因1其remove()而-1,导致两者相等不会进入迭代器的next()方法,继而不会抛出异常。但最后一个元素不会遍历)
据此,解决方法是使用迭代器,并在其中使用迭代器的移除当前元素方法,保证modCount与exceptedModCount在操作后相等。(或者使用for,但就是要避免应用这个foreach)修改代码如下:
public class ConcreteEdgesGraph<L> implements Graph<L> {
private final Set<L> vertices = new HashSet<>();
private final List<Edge<L>> edges = new ArrayList<>();
//……………………
/**
* Get all the vertices in this graph.
*
* @return the set of labels of vertices in this graph
*/
@Override public boolean remove(L vertex) {
if(vertices.contains(vertex))
{
vertices.remove(vertex);
//for(Edge listEdge:edges)
Iterator<Edge<L>> iterator = edges.iterator();
while(iterator.hasNext())
{
Edge<L> listEdge = iterator.next();//变量的作用域
if(listEdge.getSource().equals(vertex)||listEdge.getTarget().equals(vertex))
{
iterator.remove();
}
}
checkRep();
return true;
}
return false;
//throw new RuntimeException("not implemented");
}
//……………………
}