没有(有意)实现线程的 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)));
}
}
};
}
首先是我的对象:
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)));
}
}
};
}