突然间,我一直在研究的项目是在向列表中添加元素时抛出ConcurrentModificationException
,在尝试查看发生了什么事情之后,我很困惑,就像第一次发生此错误时一样。
我有一个Tournament
类,它由Event
个实例组成。事件有一个时隙列表List<Timeslot> timeslots
(当要播放比赛时)。在创建锦标赛实例时,锦标赛的时间段是所有活动的时间段。不同事件共享的时间段只添加一次。
了解我如何实例化锦标赛(我省略了无关的论点):
List<Localization> localizations = TournamentUtils.buildGenericLocalizations(1, "Court");
List<Timeslot> timeslots = TournamentUtils.buildSimpleTimeslots(8);
Event firstEvent = new Event(/*...*/, timeslots.subList(0, 2)););
Event secondEvent = new Event(/*...*/, timeslots);
Tournament tournament = new Tournament("Tournament", firstEvent, secondEvent);
这就是锦标赛构造函数的相关部分:
public Tournament(String name, List<Event> categories) {
// ...
allPlayers = new ArrayList<>();
allTimeslots = new ArrayList<>();
allLocalizations = new ArrayList<>();
events = new ArrayList<>(categories);
for (Event event : events) {
event.getPlayers().stream().filter(p -> !allPlayers.contains(p)).forEach(allPlayers::add);
event.getTimeslots().stream().filter(t -> !allTimeslots.contains(t)).forEach(allTimeslots::add);
event.getLocalizations().stream().filter(l -> !allLocalizations.contains(l)).forEach(allLocalizations::add);
event.setTournament(this);
}
// ...
}
public Tournament(String name, Event... categories) {
this(name, new ArrayList<>(Arrays.asList(categories)));
}
异常发生在event.getTimeslots().stream().filter(t -> !allTimeslots.contains(t)).forEach(allTimeslots::add);
行,我尝试使用传统的foreach
而不是使用流,但问题完全相同。
老实说,我不知道为什么会发生这种情况,因为我没有修改任何时刻正在迭代的列表。
编辑。这是我尝试的另一种方法,但我仍然得到同样的例外。
for (Event event : events) {
for (Player player : event.getPlayers())
if (!allPlayers.contains(player))
allPlayers.add(player);
for (Localization localization : event.getLocalizations())
if (!allLocalizations.contains(localization))
allLocalizations.add(localization);
for (Timeslot timeslot : event.getTimeslots())
if (!allTimeslots.contains(timeslot))
allTimeslots.add(timeslot);
event.setTournament(this);
}
答案 0 :(得分:1)
问题不在于迭代,而是将相同的列表传递给不同的事件。
Event firstEvent = new Event(/*...*/, timeslots.subList(0, 2)););
Event secondEvent = new Event(/*...*/, timeslots);
这里firstEvent和secondEvent都包含相同的列表,因为Java子列表不创建新列表,而是返回view of the original list。
对于上面使用的列表时隙,相同的列表正在传递不同的事件值,稍后用于迭代事件,以获取时隙(尽管是同一列表的不同视图)。
由于列表视图中的这种差异,会出现ConcurrentModificationException
。
改为使用new Arraylist(timeslots.sublist(0,2))
。
答案 1 :(得分:0)
首先让我们总结一下事实:您遇到ConcurrentModificationException
这可能会在您的问题中发生,如果&#34;快速失败&#34;迭代器在迭代时识别其集合被修改。
(见ConcurrentModificationException)
您的代码包含一系列事件,每个事件都提供了播放器,时间段和本地化列表。它们都被迭代,将唯一对象收集到迭代之前创建的ArrayList
个实例中。
代码不会修改任何事件&#39;收藏;它只是将元素添加到新创建的列表中。这里没问题,没有理由例外。我甚至构建了一个包含相关部分的小概念证明:两个列表,一个是另一个的子视图,另一个是ArrayList
收集元素的类似迭代 - 按预期工作而没有任何问题。
仍然有例外 - 所以必须在背后发生一些事情。
我答应了我的猜测 - 因为所有列表都是断开连接的,所有对象都不能在迭代中修改自己的集合,也不能添加到单独的列表中(除了contains
和{{1}之外没有代码被调用}),我想要么add
类在一个单独的线程上修改它的集合(是的我知道你没有提到线程)或者Event
类做了它(事件被分配到在迭代开始之前的Tournament
实例变量。)
如果您更改代码以迭代event
参数并在for循环后分配它,会发生什么?