没有(有意)实现线程的 JavaFX ConcurrentModificationException

JavaFX ConcurrentModificationException withouth (knowingly) implemtented threads

首先是我的对象:

public class Group {
     private final ObservableList<IDevice> sourceList;
     private final ObservableList<IDevice> destinationList;
     private final ObservableList<Mapping> mappingList;
...}

public class Mapping {
     private final IDevice source;
     private final IDevice destination;
     private final MappingMode mode;
     public final StringProperty sourceName = new SimpleStringProperty();
     public final StringProperty destinationName = new SimpleStringProperty();
     public final StringProperty modeName = new SimpleStringProperty();
...}

基本上,一个组包含两个 IDevices 列表(可以是源或目标)和一个映射列表,其中包含其中一个和两种模式之一(枚举)。

IDevice 列表显示在自己的列表视图中,它们之间有一个 table,表示映射(包含第一个列表中的一列、第二个列表中的一列和模式列)。

我已经通过 setItems 添加了它们,这是 ListViews 的 CellFactory

private Callback<ListView<IDevice>, ListCell<IDevice>> getFullNameDisplay() {
    return new Callback<ListView<IDevice>, ListCell<IDevice>>() {
        @Override
        public ListCell<IDevice> call(ListView<IDevice> p) {
            ListCell<IDevice> cell = new ListCell<IDevice>() {
                @Override
                protected void updateItem(IDevice t, boolean bln) {
                    super.updateItem(t, bln);
                    if (t != null) {
                        setText(t.getFullName());
                    }
                    else
                        setText("");
                }
            };
            return cell;
        }
    };
}

列设置如下:

sourceColumn.setCellValueFactory(cellData -> cellData.getValue().sourceName);
destinationColumn.setCellValueFactory(cellData -> cellData.getValue().destinationName);
modeColumn.setCellValueFactory(cellData -> cellData.getValue().modeName);

我为每个列表视图添加了两个按钮来添加和删除新项目。

当然,如果我删除了一个源或目标设备,我希望删除它的所有映射,所以我在两个列表中添加了一个 ListChangeListener:

 private ListChangeListener<IDevice> getDeviceChangeListener() {
    return (javafx.collections.ListChangeListener.Change<? extends IDevice> c) -> {
        while (c.next()) {
            if (c.wasRemoved()) {
                c.getRemoved().stream().forEach((d) -> {
                    mappingList.stream().filter((map) -> (map.getSource().equals(d) || map.getDestination().equals(d))).forEach((map) -> {
                        mappingList.remove(map);
                    });
                });
            }
        }
    };
}

这也实现了我的预期(以及我尝试过的所有重构),但我不明白为什么这会调用(大部分时间)ConcurrentModificationException,因为我还没有在我的应用程序中使用任何线程.似乎它不会每次都触发,据我所知,如果我使用线程,这可能是幸运的调度。结果是正确的

有人知道吗?

提前致谢

在同一循环中,如果您尝试迭代相同的 collection 并尝试修改相同的 collection,Java 会抛出此并发异常。

如果要修改collection,请维护不同的collection进行添加或修改。一旦跳出循环,调用Collection.addAll()或Collection.removeAll()接口。

您不能在循环访问集合时修改它,除非修改是通过迭代器完成的。在 Java 8 中,Collection class 引入了一个 removeIf(...) 方法,在这个用例中有帮助:

private ListChangeListener<IDevice> getDeviceChangeListener() {
    return (javafx.collections.ListChangeListener.Change<? extends IDevice> c) -> {
        while (c.next()) {
            if (c.wasRemoved()) {
                c.getRemoved().forEach(d -> 
                    mappingList.removeIf(map -> map.getDestination().equals(d) 
                                             || map.getSource().equals(d)));
            }
        }
    };
}