将元素添加到另一个列表

时间:2016-06-21 12:48:02

标签: java

突然间,我一直在研究的项目是在向列表中添加元素时抛出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);
}

2 个答案:

答案 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循环后分配它,会发生什么?