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

标签 java listview javafx tableview changelistener

首先是我的对象:

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();
...}

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

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

我已经通过 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);

我为每个 ListView 添加了两个按钮来添加和删除新项目。

当然,如果我删除源设备或目标设备,我希望删除其所有映射,因此我向这两个列表添加了一个 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,因为我尚未在应用程序中使用任何线程。似乎它不会每次都触发,我知道如果我使用线程,这可能是幸运的调度。.但结果是正确的

有人有线索吗?

提前致谢

最佳答案

在迭代集合时不能修改集合,除非修改是通过迭代器完成的。在 Java 8 中,Collection 类引入了 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)));
            }
        }
    };
}

关于没有(故意)实现线程的 JavaFX ConcurrentModificationException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31388657/

相关文章:

java - 通过单击 ListViewItem 打开另一个 Activity 并传递文本和图像。

PHP 将 mySQL 结果输出到可排序列中

Java/JavaFX 绕过 Final 或有效 Final,这是一个好方法吗?

javascript - HTML+javascript还是javascript+jsp?

使用 hadoop MiniDFSCluster 时出现 javax.management.InstanceAlreadyExistsException

java - 获取 Java 中的通用列表 <?> 对象的值

java - 带有 HashMap 的 ListView 只显示一个数据

java - 无限 JavaFX 坐标系 Pane

java - 显示三张卡片图像 - JavaFX

java - 哪种列表初始化方式更好