我正在使用JavaFX应用,并且正在将TableView用作GUI的一部分,如下所示:
$ sed -E 's/(rc\([0-9]+)(\s+?)([a-zA-Z0-9]+?\))/\1-\3/g' x.log
rc(200-OK) rc(403-Unauthorized) rc()
$
我给它分配了一个按钮,该按钮应该删除所选的元素。为此,我使用了一个不断给我NoSuchElementException的函数
public TableView<Meal> meals;
我在方法的最后一行调试了发现问题的方法。
研究了一段时间之后,我偶然发现了一段解决问题的代码。这是具有有效代码的方法:
public void deleteMealButtonClicked()
{
ObservableList<Meal> mealsSelected = meals.getSelectionModel().getSelectedItems();
ObservableList<Meal> allMeals = meals.getItems();
mealsSelected.forEach(allMeals::remove);
}
我的问题是,为什么第二种方法有效,而不是第一种有效?除了在第二种方法中将选定的行添加到ArrayList之外,它们根本不是同一回事吗?
谢谢。
答案 0 :(得分:1)
TableView
的选择模型必须遵守ObservableList
属性中的items
,才能正确反映表的状态。换句话说,如果从items
中删除了元素,则还必须从选择模型的选定项中删除它。
这导致您在迭代selectedMeals
列表以从meals
列表中删除包含的元素的同时间接修改java.util
列表。在许多ConcurrentModificationException
集合中,迭代器都是快速失败的,这将导致NullPointerException
;我猜选择模型不是快速失败的,所以您只是看到不确定的行为(以selectedMeals
的形式)。
第二个选项起作用是因为您首先制作了removeAll
的副本,然后迭代了该副本。
您在注释中询问不创建副本的方法;不幸的是,我想不出一个。不过,您可以使用ObservableList<Meal> selected = ...;
// List.copyOf is a Java 10 method (and doesn't accept null elements)
table.getItems().removeAll(List.copyOf(selected));
// Pre Java 10 you can use toArray since ObservableList overloads removeAll
// with a version that takes a varargs parameter
table.getItems().removeAll(selected.toArray(new Meal[0]));
// or use the copy constructor of many collection classes
table.getItems().removeAll(new ArrayList<>(selected));
// In Java 11, toArray can be replaced with
table.getItems().removeAll(selected.toArray(Meal[]::new));
来简化代码。
config.xml