java.util.ConcurrentModificationException背后的原因

时间:2014-08-16 04:51:03

标签: java exception

Stack<String> ST = new Stack<String>();
ST.add("one");
ST.add("two");
ST.add("three");
ST.add("four");

ST.addAll(ST.subList(0,2));
System.out.println(ST);

遵循简单代码给j ava.util.ConcurrentModificationException。我无法弄清楚这个例外背后的原因是什么?

3 个答案:

答案 0 :(得分:2)

List.subList将视图返回到容器中,而不是副本。

来自documentation

  

如果是,则此方法返回的列表的语义将变为未定义   支持列表(即,该列表)以任何方式在结构上被修改   除了通过返回的列表。 (结构修改是那些   这会改变这个列表的大小,或以其他方式扰乱它   方式,正在进行的迭代可能会产生不正确的结果。)

因此,当您调用addAll时,它会修改基础容器并更改其大小,从而使子列表无效。但它仍在尝试迭代子列表以继续添加内容。

答案 1 :(得分:1)

documentation for the subList method说:

  

如果支持列表(即此列表)以除了通过返回的List之外的任何方式进行结构修改,则此方法返回的List的语义将变为未定义。 (结构修改是那些改变列表大小的修改,或以其他方式扰乱它,使得正在进行的迭代可能产生不正确的结果。)

换句话说,当您访问子列表时,您不允许更改真实列表。

致电时

ST.addAll(ST.subList(0,2));

您导致addAll遍历子列表并同时修改真实列表(ST)。 addAll将从子列表中获取第一个元素并将其附加到ST,这是一种结构修改,可使基于ST的所有子列表无效。然后addAll将尝试从子列表中获取第二个元素,但由于刚刚对ST进行了更改,子列表现在无效,因此它会抛出异常。

答案 2 :(得分:0)

问题发生在这里:

ST.addAll(ST.subList(0,2));

问题在于引发了以下事情:

  1. ST.subList(0,2)调用会为原始列表创建一个包装器。

  2. ST.addAll调用为子列表创建Iterator,该列表实际上是原始列表ListIterator的包装。

  3. 然后,在迭代子列表(以及列表)时,它会将找到的元素推回到原始列表中。

  4. 最后一个是并发修改。

    请注意,这是固有的,而不仅仅是实现细节。 subList方法返回原始List视图。因此,您的语句本身就是在同时迭代和修改相同的(底层)列表。