我查了listiterator类的remove方法的代码,不明白为什么在运行列表时删除了一个项之后,在试图获取remove之后的下一个元素时会抛出异常。以下是我读到的资料来源:
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
SubList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = ArrayList.this.modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
我不明白的是:
为什么remove方法获取lastret而不是cursor。我们要删除当前项而不是已传递的项?
2.为什么lastret设置为-1?
3.下一行:expectedmodcount=arraylist.this.modcount;将expectedmodcount设置为等于modcount,这样在下一次迭代中,当检查两个变量是否相等时,if将“say”true,一切正常。我在网上读了很多文章,也有一些答案,但还是听不懂
因为我得到的响应是代码不会导致运行时异常,下面是导致异常的代码:
public class Testing
{
public static void main(String[] args)
{
List <String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
Iterator<String> iterator = list.listIterator();
while (iterator.hasNext())
{
String st = iterator.next();
if (st.equals("c"))
{
list.remove(index);
}
else
{
System.out.println(st);
}
}
}
1条答案
按热度按时间qkf9rpyu1#
iterator.remove()的javadoc说明:
因此,在调用此方法时,光标指向下一个元素,因此使用lastret。
-1表示未找到。它被设置为-1,因为您刚刚删除了lastret,因此它不再存在。这意味着如果不先调用next(),就不能再次调用remove():如果查看next(),它将更新lastret
调用arraylist.this.remove()更改modcount(modcount由abstractlist管理,abstractlist是您正在查看的arraylist迭代器的一个超类)。因为这个更改是由方法本身引起的,所以我们知道这是一个有效的更改(换句话说,我们只是请求更改列表)。因此局部变量(expectedmodcount)被更新。如果这个列表被迭代器的另一个示例修改,那么abstractlist的modcount将改变,但是属于当前示例的expectedmodcount不会改变。这样,当前示例将知道存在并发修改。
额外问题:
我理解。请回答一个小问题。。如果有两个不同的线程在同一个列表上运行for/while循环(而不是迭代器)。其中一个正在删除一个项目,在这种情况下不会发生异常?只有在使用迭代器时系统才是“安全的”?
多线程不是一个“小”问题:)
简而言之,不,这两种情况都不安全。无论使用什么循环,当一个线程正在检查是否可以安全地执行另一个循环时(无论是.hasnext()还是i<size()),另一个线程可以同时删除/添加元素。因此,如果使用for循环,则不会出现并发修改异常,但可能会出现索引越界异常。
你应该自己试试。下面是一些黑客示例(我使用linkedlist进行快速删除操作,因为使用迭代器从列表开头删除对象非常慢)
迭代器(抛出concurrentmodificationexception):
替换为for i循环(抛出indexoutofboundsexception):
要避免这些异常,可以做的一件事是使用同步块,它确保一次只允许1个线程在内部执行-在进入同步块后,需要再次检查条件是否仍然为真:
java还有并发集合,您可以使用这些集合使多个线程能够进行并行计算。
为每个线程提供自己的迭代器需要更改为类似concurrentlinkedqueue的内容,以避免并发修改异常。但是,代码给出了一个非常错误的结果:
打印出来的
所以迭代器似乎不是线程安全的:)
如果我们去掉迭代器:
这样更好:
把锁放回去:
似乎有用